From 6fe57399c489d039454774c7bd32a686350b5aae Mon Sep 17 00:00:00 2001 From: Jason Pochanke Date: Mon, 29 Jun 2026 15:56:30 +0200 Subject: [PATCH] init --- README.md | 24 + designdoc.md | 116 + gleam.toml | 21 + manifest.toml | 21 + markdown2.example | 7 + src/definition.gleam | 28 + src/markdown2.gleam | 75 + src/mode.gleam | 6 + src/parser.gleam | 71 + ui/index.html | 8 + .../filepath/_gleam_artefacts/filepath.cache | Bin 0 -> 15471 bytes .../_gleam_artefacts/filepath.cache_meta | Bin 0 -> 1665 bytes ui/javascript/filepath/filepath.erl | 425 +++ ui/javascript/filepath/filepath.mjs | 527 +++ ui/javascript/filepath/filepath_ffi.erl | 9 + ui/javascript/filepath/filepath_ffi.mjs | 6 + ui/javascript/filepath/gleam.mjs | 1 + ui/javascript/fingerprint | 1 + .../_gleam_artefacts/gleam@json.cache | Bin 0 -> 19410 bytes .../_gleam_artefacts/gleam@json.cache_meta | Bin 0 -> 1554 bytes ui/javascript/gleam_json/gleam.mjs | 1 + ui/javascript/gleam_json/gleam/json.mjs | 325 ++ ui/javascript/gleam_json/gleam@json.erl | 304 ++ ui/javascript/gleam_json/gleam_json_ffi.erl | 66 + ui/javascript/gleam_json/gleam_json_ffi.mjs | 201 ++ .../_gleam_artefacts/gleam@bit_array.cache | Bin 0 -> 15554 bytes .../gleam@bit_array.cache_meta | Bin 0 -> 1253 bytes .../_gleam_artefacts/gleam@bool.cache | Bin 0 -> 11052 bytes .../_gleam_artefacts/gleam@bool.cache_meta | Bin 0 -> 1245 bytes .../_gleam_artefacts/gleam@bytes_tree.cache | Bin 0 -> 14109 bytes .../gleam@bytes_tree.cache_meta | Bin 0 -> 903 bytes .../_gleam_artefacts/gleam@dict.cache | Bin 0 -> 31186 bytes .../_gleam_artefacts/gleam@dict.cache_meta | Bin 0 -> 2409 bytes .../_gleam_artefacts/gleam@dynamic.cache | Bin 0 -> 7635 bytes .../_gleam_artefacts/gleam@dynamic.cache_meta | Bin 0 -> 475 bytes .../gleam@dynamic@decode.cache | Bin 0 -> 69111 bytes .../gleam@dynamic@decode.cache_meta | Bin 0 -> 4725 bytes .../_gleam_artefacts/gleam@float.cache | Bin 0 -> 25617 bytes .../_gleam_artefacts/gleam@float.cache_meta | Bin 0 -> 2564 bytes .../_gleam_artefacts/gleam@function.cache | Bin 0 -> 855 bytes .../gleam@function.cache_meta | Bin 0 -> 73 bytes .../_gleam_artefacts/gleam@int.cache | Bin 0 -> 33208 bytes .../_gleam_artefacts/gleam@int.cache_meta | Bin 0 -> 3419 bytes .../_gleam_artefacts/gleam@io.cache | Bin 0 -> 3037 bytes .../_gleam_artefacts/gleam@io.cache_meta | Bin 0 -> 273 bytes .../_gleam_artefacts/gleam@list.cache | Bin 0 -> 96344 bytes .../_gleam_artefacts/gleam@list.cache_meta | Bin 0 -> 9274 bytes .../_gleam_artefacts/gleam@option.cache | Bin 0 -> 15072 bytes .../_gleam_artefacts/gleam@option.cache_meta | Bin 0 -> 1357 bytes .../_gleam_artefacts/gleam@order.cache | Bin 0 -> 6526 bytes .../_gleam_artefacts/gleam@order.cache_meta | Bin 0 -> 629 bytes .../_gleam_artefacts/gleam@pair.cache | Bin 0 -> 3292 bytes .../_gleam_artefacts/gleam@pair.cache_meta | Bin 0 -> 369 bytes .../_gleam_artefacts/gleam@result.cache | Bin 0 -> 19673 bytes .../_gleam_artefacts/gleam@result.cache_meta | Bin 0 -> 1699 bytes .../_gleam_artefacts/gleam@set.cache | Bin 0 -> 18974 bytes .../_gleam_artefacts/gleam@set.cache_meta | Bin 0 -> 1773 bytes .../_gleam_artefacts/gleam@string.cache | Bin 0 -> 41832 bytes .../_gleam_artefacts/gleam@string.cache_meta | Bin 0 -> 4017 bytes .../_gleam_artefacts/gleam@string_tree.cache | Bin 0 -> 14962 bytes .../gleam@string_tree.cache_meta | Bin 0 -> 891 bytes .../_gleam_artefacts/gleam@uri.cache | Bin 0 -> 37553 bytes .../_gleam_artefacts/gleam@uri.cache_meta | Bin 0 -> 3269 bytes ui/javascript/gleam_stdlib/dict.mjs | 710 ++++ ui/javascript/gleam_stdlib/gleam.mjs | 1 + .../gleam_stdlib/gleam/bit_array.mjs | 286 ++ ui/javascript/gleam_stdlib/gleam/bool.mjs | 295 ++ .../gleam_stdlib/gleam/bytes_tree.mjs | 225 ++ ui/javascript/gleam_stdlib/gleam/dict.mjs | 452 +++ ui/javascript/gleam_stdlib/gleam/dynamic.mjs | 35 + .../gleam_stdlib/gleam/dynamic/decode.mjs | 963 ++++++ ui/javascript/gleam_stdlib/gleam/float.mjs | 528 +++ ui/javascript/gleam_stdlib/gleam/function.mjs | 6 + ui/javascript/gleam_stdlib/gleam/int.mjs | 764 +++++ ui/javascript/gleam_stdlib/gleam/io.mjs | 8 + ui/javascript/gleam_stdlib/gleam/list.mjs | 3029 +++++++++++++++++ ui/javascript/gleam_stdlib/gleam/option.mjs | 386 +++ ui/javascript/gleam_stdlib/gleam/order.mjs | 175 + ui/javascript/gleam_stdlib/gleam/pair.mjs | 88 + ui/javascript/gleam_stdlib/gleam/result.mjs | 448 +++ ui/javascript/gleam_stdlib/gleam/set.mjs | 409 +++ ui/javascript/gleam_stdlib/gleam/string.mjs | 699 ++++ .../gleam_stdlib/gleam/string_tree.mjs | 128 + ui/javascript/gleam_stdlib/gleam/uri.mjs | 1147 +++++++ .../gleam_stdlib/gleam@bit_array.erl | 347 ++ ui/javascript/gleam_stdlib/gleam@bool.erl | 334 ++ .../gleam_stdlib/gleam@bytes_tree.erl | 211 ++ ui/javascript/gleam_stdlib/gleam@dict.erl | 513 +++ ui/javascript/gleam_stdlib/gleam@dynamic.erl | 105 + .../gleam_stdlib/gleam@dynamic@decode.erl | 1121 ++++++ ui/javascript/gleam_stdlib/gleam@float.erl | 711 ++++ ui/javascript/gleam_stdlib/gleam@function.erl | 18 + ui/javascript/gleam_stdlib/gleam@int.erl | 972 ++++++ ui/javascript/gleam_stdlib/gleam@io.erl | 76 + ui/javascript/gleam_stdlib/gleam@list.erl | 2716 +++++++++++++++ ui/javascript/gleam_stdlib/gleam@option.erl | 381 +++ ui/javascript/gleam_stdlib/gleam@order.erl | 188 + ui/javascript/gleam_stdlib/gleam@pair.erl | 104 + ui/javascript/gleam_stdlib/gleam@result.erl | 500 +++ ui/javascript/gleam_stdlib/gleam@set.erl | 430 +++ ui/javascript/gleam_stdlib/gleam@string.erl | 1006 ++++++ .../gleam_stdlib/gleam@string_tree.erl | 202 ++ ui/javascript/gleam_stdlib/gleam@uri.erl | 1042 ++++++ ui/javascript/gleam_stdlib/gleam_stdlib.erl | 554 +++ ui/javascript/gleam_stdlib/gleam_stdlib.mjs | 1150 +++++++ .../gleeunit/_gleam_artefacts/gleeunit.cache | Bin 0 -> 9318 bytes .../_gleam_artefacts/gleeunit.cache_meta | Bin 0 -> 479 bytes .../gleeunit@internal@gleam_panic.cache | Bin 0 -> 15775 bytes .../gleeunit@internal@gleam_panic.cache_meta | Bin 0 -> 278 bytes .../gleeunit@internal@reporting.cache | Bin 0 -> 14874 bytes .../gleeunit@internal@reporting.cache_meta | Bin 0 -> 1277 bytes .../_gleam_artefacts/gleeunit@should.cache | Bin 0 -> 4261 bytes .../gleeunit@should.cache_meta | Bin 0 -> 397 bytes ui/javascript/gleeunit/gleam.mjs | 1 + ui/javascript/gleeunit/gleeunit.erl | 89 + ui/javascript/gleeunit/gleeunit.mjs | 62 + .../gleeunit/internal/gleam_panic.mjs | 180 + .../internal/gleeunit_gleam_panic_ffi.erl | 49 + .../internal/gleeunit_gleam_panic_ffi.mjs | 91 + .../gleeunit/gleeunit/internal/reporting.mjs | 257 ++ ui/javascript/gleeunit/gleeunit/should.mjs | 135 + .../gleeunit@internal@gleam_panic.erl | 56 + .../gleeunit/gleeunit@internal@reporting.erl | 343 ++ ui/javascript/gleeunit/gleeunit@should.erl | 153 + ui/javascript/gleeunit/gleeunit_ffi.erl | 21 + ui/javascript/gleeunit/gleeunit_ffi.mjs | 100 + ui/javascript/gleeunit/gleeunit_progress.erl | 72 + .../_gleam_artefacts/definition.cache | Bin 0 -> 8194 bytes .../_gleam_artefacts/definition.cache_meta | Bin 0 -> 185 bytes .../_gleam_artefacts/markdown2.cache | Bin 0 -> 3545 bytes .../_gleam_artefacts/markdown2.cache_meta | Bin 0 -> 522 bytes .../_gleam_artefacts/markdown2_test.cache | Bin 0 -> 1139 bytes .../markdown2_test.cache_meta | Bin 0 -> 129 bytes .../markdown2/_gleam_artefacts/mode.cache | Bin 0 -> 1674 bytes .../_gleam_artefacts/mode.cache_meta | Bin 0 -> 77 bytes .../markdown2/_gleam_artefacts/parser.cache | Bin 0 -> 3623 bytes .../_gleam_artefacts/parser.cache_meta | Bin 0 -> 516 bytes ui/javascript/markdown2/definition.mjs | 103 + ui/javascript/markdown2/gleam.mjs | 1 + ui/javascript/markdown2/markdown2.mjs | 77 + ui/javascript/markdown2/markdown2_test.mjs | 34 + ui/javascript/markdown2/mode.mjs | 17 + ui/javascript/markdown2/parser.mjs | 367 ++ ui/javascript/prelude.mjs | 1561 +++++++++ .../_gleam_artefacts/simplifile.cache | Bin 0 -> 65770 bytes .../_gleam_artefacts/simplifile.cache_meta | Bin 0 -> 3248 bytes ui/javascript/simplifile/gleam.mjs | 1 + ui/javascript/simplifile/simplifile.erl | 903 +++++ ui/javascript/simplifile/simplifile.mjs | 1053 ++++++ ui/javascript/simplifile/simplifile_erl.erl | 244 ++ ui/javascript/simplifile/simplifile_js.mjs | 412 +++ ui/main.js | 34 + 152 files changed, 32122 insertions(+) create mode 100644 README.md create mode 100644 designdoc.md create mode 100644 gleam.toml create mode 100644 manifest.toml create mode 100644 markdown2.example create mode 100644 src/definition.gleam create mode 100644 src/markdown2.gleam create mode 100644 src/mode.gleam create mode 100644 src/parser.gleam create mode 100644 ui/index.html create mode 100644 ui/javascript/filepath/_gleam_artefacts/filepath.cache create mode 100644 ui/javascript/filepath/_gleam_artefacts/filepath.cache_meta create mode 100644 ui/javascript/filepath/filepath.erl create mode 100644 ui/javascript/filepath/filepath.mjs create mode 100644 ui/javascript/filepath/filepath_ffi.erl create mode 100644 ui/javascript/filepath/filepath_ffi.mjs create mode 100644 ui/javascript/filepath/gleam.mjs create mode 100644 ui/javascript/fingerprint create mode 100644 ui/javascript/gleam_json/_gleam_artefacts/gleam@json.cache create mode 100644 ui/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_meta create mode 100644 ui/javascript/gleam_json/gleam.mjs create mode 100644 ui/javascript/gleam_json/gleam/json.mjs create mode 100644 ui/javascript/gleam_json/gleam@json.erl create mode 100644 ui/javascript/gleam_json/gleam_json_ffi.erl create mode 100644 ui/javascript/gleam_json/gleam_json_ffi.mjs create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache create mode 100644 ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_meta create mode 100644 ui/javascript/gleam_stdlib/dict.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/bit_array.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/bool.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/bytes_tree.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/dict.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/dynamic.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/dynamic/decode.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/float.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/function.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/int.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/io.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/list.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/option.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/order.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/pair.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/result.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/set.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/string.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/string_tree.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam/uri.mjs create mode 100644 ui/javascript/gleam_stdlib/gleam@bit_array.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@bool.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@bytes_tree.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@dict.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@dynamic.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@dynamic@decode.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@float.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@function.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@int.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@io.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@list.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@option.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@order.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@pair.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@result.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@set.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@string.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@string_tree.erl create mode 100644 ui/javascript/gleam_stdlib/gleam@uri.erl create mode 100644 ui/javascript/gleam_stdlib/gleam_stdlib.erl create mode 100644 ui/javascript/gleam_stdlib/gleam_stdlib.mjs create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit.cache create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_meta create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_meta create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_meta create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache create mode 100644 ui/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache_meta create mode 100644 ui/javascript/gleeunit/gleam.mjs create mode 100644 ui/javascript/gleeunit/gleeunit.erl create mode 100644 ui/javascript/gleeunit/gleeunit.mjs create mode 100644 ui/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs create mode 100644 ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl create mode 100644 ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs create mode 100644 ui/javascript/gleeunit/gleeunit/internal/reporting.mjs create mode 100644 ui/javascript/gleeunit/gleeunit/should.mjs create mode 100644 ui/javascript/gleeunit/gleeunit@internal@gleam_panic.erl create mode 100644 ui/javascript/gleeunit/gleeunit@internal@reporting.erl create mode 100644 ui/javascript/gleeunit/gleeunit@should.erl create mode 100644 ui/javascript/gleeunit/gleeunit_ffi.erl create mode 100644 ui/javascript/gleeunit/gleeunit_ffi.mjs create mode 100644 ui/javascript/gleeunit/gleeunit_progress.erl create mode 100644 ui/javascript/markdown2/_gleam_artefacts/definition.cache create mode 100644 ui/javascript/markdown2/_gleam_artefacts/definition.cache_meta create mode 100644 ui/javascript/markdown2/_gleam_artefacts/markdown2.cache create mode 100644 ui/javascript/markdown2/_gleam_artefacts/markdown2.cache_meta create mode 100644 ui/javascript/markdown2/_gleam_artefacts/markdown2_test.cache create mode 100644 ui/javascript/markdown2/_gleam_artefacts/markdown2_test.cache_meta create mode 100644 ui/javascript/markdown2/_gleam_artefacts/mode.cache create mode 100644 ui/javascript/markdown2/_gleam_artefacts/mode.cache_meta create mode 100644 ui/javascript/markdown2/_gleam_artefacts/parser.cache create mode 100644 ui/javascript/markdown2/_gleam_artefacts/parser.cache_meta create mode 100644 ui/javascript/markdown2/definition.mjs create mode 100644 ui/javascript/markdown2/gleam.mjs create mode 100644 ui/javascript/markdown2/markdown2.mjs create mode 100644 ui/javascript/markdown2/markdown2_test.mjs create mode 100644 ui/javascript/markdown2/mode.mjs create mode 100644 ui/javascript/markdown2/parser.mjs create mode 100644 ui/javascript/prelude.mjs create mode 100644 ui/javascript/simplifile/_gleam_artefacts/simplifile.cache create mode 100644 ui/javascript/simplifile/_gleam_artefacts/simplifile.cache_meta create mode 100644 ui/javascript/simplifile/gleam.mjs create mode 100644 ui/javascript/simplifile/simplifile.erl create mode 100644 ui/javascript/simplifile/simplifile.mjs create mode 100644 ui/javascript/simplifile/simplifile_erl.erl create mode 100644 ui/javascript/simplifile/simplifile_js.mjs create mode 100644 ui/main.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b52c9d --- /dev/null +++ b/README.md @@ -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 . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +``` diff --git a/designdoc.md b/designdoc.md new file mode 100644 index 0000000..7163517 --- /dev/null +++ b/designdoc.md @@ -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 +----- diff --git a/gleam.toml b/gleam.toml new file mode 100644 index 0000000..9a362f7 --- /dev/null +++ b/gleam.toml @@ -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" diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..564292a --- /dev/null +++ b/manifest.toml @@ -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" } diff --git a/markdown2.example b/markdown2.example new file mode 100644 index 0000000..145590b --- /dev/null +++ b/markdown2.example @@ -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 >- diff --git a/src/definition.gleam b/src/definition.gleam new file mode 100644 index 0000000..68b91ea --- /dev/null +++ b/src/definition.gleam @@ -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), + ) +} diff --git a/src/markdown2.gleam b/src/markdown2.gleam new file mode 100644 index 0000000..935d5d6 --- /dev/null +++ b/src/markdown2.gleam @@ -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)) +} diff --git a/src/mode.gleam b/src/mode.gleam new file mode 100644 index 0000000..fc3c1f3 --- /dev/null +++ b/src/mode.gleam @@ -0,0 +1,6 @@ +pub type Type { + Hirachy + Truth + Marking + Quote +} diff --git a/src/parser.gleam b/src/parser.gleam new file mode 100644 index 0000000..d462ffc --- /dev/null +++ b/src/parser.gleam @@ -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 != "" }) +} diff --git a/ui/index.html b/ui/index.html new file mode 100644 index 0000000..c90d948 --- /dev/null +++ b/ui/index.html @@ -0,0 +1,8 @@ + +
+ diff --git a/ui/javascript/filepath/_gleam_artefacts/filepath.cache b/ui/javascript/filepath/_gleam_artefacts/filepath.cache new file mode 100644 index 0000000000000000000000000000000000000000..fdb5138542cbdaac88f6508feee5c8bd9c8bb226 GIT binary patch literal 15471 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 literal 0 HcmV?d00001 diff --git a/ui/javascript/filepath/filepath.erl b/ui/javascript/filepath/filepath.erl new file mode 100644 index 0000000..e774982 --- /dev/null +++ b/ui/javascript/filepath/filepath.erl @@ -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>>, _} -> + <>; + + {_, _} -> + _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, + <>, + <<"/"/utf8>> + ); + + [First | Rest@1] -> + get_directory_name(Rest@1, Acc, <>); + + [] -> + 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. diff --git a/ui/javascript/filepath/filepath.mjs b/ui/javascript/filepath/filepath.mjs new file mode 100644 index 0000000..40faa05 --- /dev/null +++ b/ui/javascript/filepath/filepath.mjs @@ -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; + } +} diff --git a/ui/javascript/filepath/filepath_ffi.erl b/ui/javascript/filepath/filepath_ffi.erl new file mode 100644 index 0000000..2dde1aa --- /dev/null +++ b/ui/javascript/filepath/filepath_ffi.erl @@ -0,0 +1,9 @@ +-module(filepath_ffi). + +-export([is_windows/0]). + +is_windows() -> + case os:type() of + {win32, _} -> true; + _ -> false + end. diff --git a/ui/javascript/filepath/filepath_ffi.mjs b/ui/javascript/filepath/filepath_ffi.mjs new file mode 100644 index 0000000..8919959 --- /dev/null +++ b/ui/javascript/filepath/filepath_ffi.mjs @@ -0,0 +1,6 @@ +export function is_windows() { + return ( + globalThis?.process?.platform === "win32" || + globalThis?.Deno?.build?.os === "windows" + ); +} diff --git a/ui/javascript/filepath/gleam.mjs b/ui/javascript/filepath/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/ui/javascript/filepath/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/ui/javascript/fingerprint b/ui/javascript/fingerprint new file mode 100644 index 0000000..bc085ca --- /dev/null +++ b/ui/javascript/fingerprint @@ -0,0 +1 @@ +1.17.0 0 \ No newline at end of file diff --git a/ui/javascript/gleam_json/_gleam_artefacts/gleam@json.cache b/ui/javascript/gleam_json/_gleam_artefacts/gleam@json.cache new file mode 100644 index 0000000000000000000000000000000000000000..c1e4d954e8be196631df6b4b5a658b8845e048f4 GIT binary patch literal 19410 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 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_meta b/ui/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..00dd8197b7222766439b5ca90ee6c6f49f62c337 GIT binary patch literal 1554 zcmZwHZ)jFk7y$6I+@_{Aoo@MOPLbN$hMP=fGSo35`cT0HjrKugZSS^M-)*+NRt9OM z)M6r9b5ZDwJ_rRXur!xaV6jRQDq;Q!Nl@89Lg!Q>&Gox&Uj{w!mFwcQ$wy?C^3eFomgbtKcueHY7HYbvY<$q*4 zQH^*##$rE74zpy(yoLR3mlG>fqG6*EeNQy)Xj;((q8CLEjPAL`Z6+9> z%3Ml$nnvDZFI`+E>wyqvQ@|q1Y36m>*~tg|zyQ}7CQJxn0Wq3*k!^Hxlz#qUgpeA- zH0Dvtb0pbMFUL5}FI?m%*=hb68`;efj?&9biqk_V;WZ9%gg#DkmYEYn$fKB2DtL)5 zy7`#TIY+L}%A=5#tl<^9_?0{4Og4`BJWD5E@ds1%MF!K!q?}cBaGs0&$q@G`m=Z!6 z6+A(LH`vY&zUD5uI%hu5P{n?(aF-D>H1rG>P{qq^V>`Y4P2s~K6j9FxUS%sC^zaRR zoMebpt1F#}EMpV9*~4cX;skx1;Rg3eofg6*idexaD%i+DPVgOGr%C}nU*4R$fby$+ z9OF2HEVNIQv7B~xvX77G;R2Vr#&z!TSXKy&Sgb;jegE?o=jUUg<|UI<^b2onrn}tfJKzEiyjWsOCJ{)W@=6dvuI?)7;pG5{spP7;_d(d literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_json/gleam.mjs b/ui/javascript/gleam_json/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/ui/javascript/gleam_json/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/ui/javascript/gleam_json/gleam/json.mjs b/ui/javascript/gleam_json/gleam/json.mjs new file mode 100644 index 0000000..3111ddc --- /dev/null +++ b/ui/javascript/gleam_json/gleam/json.mjs @@ -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); }, + ), + ); +} diff --git a/ui/javascript/gleam_json/gleam@json.erl b/ui/javascript/gleam_json/gleam@json.erl new file mode 100644 index 0000000..8a33e49 --- /dev/null +++ b/ui/javascript/gleam_json/gleam@json.erl @@ -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 + ) + ). diff --git a/ui/javascript/gleam_json/gleam_json_ffi.erl b/ui/javascript/gleam_json/gleam_json_ffi.erl new file mode 100644 index 0000000..06a26a0 --- /dev/null +++ b/ui/javascript/gleam_json/gleam_json_ffi.erl @@ -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. diff --git a/ui/javascript/gleam_json/gleam_json_ffi.mjs b/ui/javascript/gleam_json/gleam_json_ffi.mjs new file mode 100644 index 0000000..1d8d3ff --- /dev/null +++ b/ui/javascript/gleam_json/gleam_json_ffi.mjs @@ -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; +} diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache new file mode 100644 index 0000000000000000000000000000000000000000..dd36389d9d275e9491d2a0ed0df3eb2466e32b3c GIT binary patch literal 15554 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 SDu@_N0kt%-hIMI?9v=a4$E+;? literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache new file mode 100644 index 0000000000000000000000000000000000000000..3a4cc10e4b96bdeac86221e177cc89e3617d5031 GIT binary patch literal 11052 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 F{}Wt3K??u? literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..aba21b22f49db4d156d9b665e1f90c89f191e764 GIT binary patch literal 1245 zcmX}sTWAPS7zgk(!)CK#u8q)W3PnVw=2k+iEXE#OhX>-pWwx+P#$1zIhHOjmu(TEl zG3GjJiM1&YD`YNd%l(qJ_@(jHuTJMX5C6WtPW<}=t3!K5{5!1Ox8lvp2_;8H{J#ld zo@O>%sHV5x8cHbT6>k~XA%rDlv4PF(q?q%RafJpNc|i*?ks&0|hapU57HMoEkL~1h zhX=Hg+RRm>C9ps+2oMRDT*oQBDZLyiFbS>AxahoGJ=`RWj@QvV;6g=;3ievripj_q?Inw zAq-;_W0}HYR>4&q2; zGBcRXRtnfnA(yy9HMKnBH&L;2(2vQ?U^ba-r;z;=QOSL3_(f#b5IWJBRHl+n23Ztx zh@+HJ&133mB_=L}L5yKM6G&k@SDhPGI5s6 zT;l-^M0F3L7l|a1%tk8tz!!cH)x(VF$8eUD!vT&_#5pSYN=$-%WhA4SL?-JwPBE9L zDVWP XX!J^igekr$|5 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache new file mode 100644 index 0000000000000000000000000000000000000000..48786b3ba6e123af1cbdcc27eaa07ceef333ed1f GIT binary patch literal 14109 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 Y=xVdo*Bp%GL^~@u$m%q!JF1!VU#TYwCIA2c literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..7b84352673a0d8b5f48f5b51cf8057d1e3fe7756 GIT binary patch literal 903 zcmY+?J4h8!7y#g-Wo5oft)Zf($ifG;Bo2i^jnRXKTEyz6H?-oagXDv(w3Z6;u_)Tm z!-5C{2h)@W2Z=zXprVGLP$)!5DwOEEaTgDKoWuDK|M@xR&zx$EhQvr(kyvCU=8CFF zd3Dk0Xl+?}ELL9Mv@(Qt)-DTIMQdt1>^@-qvPeyBEPA1$tTq;jJQSRyyza{1t{dsC zqlts@=`g*_us11$R$ep7w&W0^3^2z}_9^owFBl_J-V*lHNW9E=LGe4Mm}ZWoln`>s zr;s8VxI;V77-5t#ezC~LRUvHR9CzuUlL_81%MXfIhY+EHN_x1*Fk@t|aVNXk!&xf1 zN*hC@rG~JXgVa&aWf~Y@kQsLRg94g(#ABZDnJ?s~g>ZtC^fS#{-jSLfLI!!{bBf#a z&`Tfh`NVIQD9H$+lrpNh$R)bzV~Pd-ke4YZEnMR|gADPVEbW!U0orJ%lP<=Y;48Ci zT^GW3N;$$Yj?=-GkhmS7p8KQCR%vTH|9ys_CD+)p8}e>MK?Ws;3FA1{(?i) mGr|IkEK!&n!Wk-va-RG2^NMli*|H&o9Td|@bAp!tKmGw(tcKwL literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache new file mode 100644 index 0000000000000000000000000000000000000000..02626f2c1c09f83e8a2beceab4144e5257ce821d GIT binary patch literal 31186 zcmdU&37A}EneR`9y;b%-f+rmyouui`0tAGBm=FkRPzbnSOjo*3r^~LcYHLZ-U_fvf zhDC9J5pjlLabz5EW*kHYd1eG*1P2&YRJaJD7w?q^ml^d6qr%nu`+w(qPo1hd)!o<$ zkN4#H_1V7f{kHdezx7nQg80~2jPhgsLxsVb?q8dJy1rT)Di$`(^3Pu9&uc6BQne73 zYAf-S!$epciMqnm^PBQ# z4OR-{wXl}2Y>aAs;hJ(H>g2btmh5QDcv=2yWqFdUN?%u3m_&lmE0NxA;BO_$*KnJ! zh9mh+h0;diQ7n|Ad?oCua$Ag!#D6b>3Z>DoQm@wLhqa0EsIMzL8PC3u!cF;NJwo1K zc`T|DaqbIFXtk`Q@u)Vh8jk0yK3?Tg6qbiw^oHvtm+Wd^*N!S84wbpi{p9y)&|W&N z1+hmXmPJhiWQ?b+)%HDC`00`i*m2lB*EuoH9tmyO0ohpr>rM`o{kL$ocClS2p$! zl?VIB@+BCr_KnoWit|Gi$XK~Tohy{M9?L5ch~8Q1#9_Mf=5o0hKZ&i2>5aW#Td7C( zKG?}$t;!dxji-~E8nb3*#nOsxT77nHcm)|YUM9CKth76e^O;e#UaVOR%|-g&uI+rr zXxf#Y+C7(+no}y3a>bs@jp5^BSs}eK!B%0dqMCPk*}?l;Q)tRrbCU5oIKq3el*UT$ zAG-LoylFQnCjPy%gWyjgH{hRyr$$nD%p$c-^^rR0E0wDo8w;DFlB@QeD@Qdp#on9D zuPQ(#LYYgAx>FsG1`ET5!MLXMg)6Bba<{oss9`QtCu$>k`J=|5`z2=!Tm`fkNpvFC zwHj15GqGKnRjNn>P-&`Bt{SO6#kEJtnmacArC#S0`$muX8Q59?{D^oiw zAuCo>e-{Mkgg_*T+u9@-@A=DiM;*AEwpsW!FvIX?37P&08Y&nisGUpeQohO&qu@V&$npBUXs6=2~OL(#O2IOVahmd z#~}9f=}5XV>kB1mv}KF(sgs+GLNnX6biW|D2r>%IoQ&6Nx!3s6`}$l@wr}kH)r0+` zp{QD`)ae;*ijt1o>LhB4o(Zy|Jg22--)U)3EMR)MIB8=vouFQ|uuMhAB09o8*PQM* zy~tUd<-%$%?PNK9FY~1=YPTNpt>d?c1;OgW$;gt=)o_LDIjdnP(SaYLZF5$T`|*6C zqK2Z;FjUonr*FKWK!<3ftTry_7gaFhvaYLLR~XYR^X;F9n+vrOXW(7G&UFlFZ#I;9 zs?e(Log7cZ0lNW0l`c4)KNp=^ew0>%e%xR*6#2eeCp%t@=v;=&r#9qWp`31Lb1Fl> zB)0j3TrS1tC#{1kp`JWl*!dyWf6~UHO$KdERvXb&$Ze)CF0|?Dys1T26LFm{Z&RaB z_AUFb}>m+*%cEq=*)=PtJwA^)2u2fMBQ&gd$iaW ziWSllf3KjL4%3>B?YNApMbxBCDSdh=nM?&b2C<8eer!AYQ*$}yH zH*9jPBY$iV{2S!%^nU+H3|nkGt>?;+u`3=ZGi@;x(VZG&lEPU)Szp($){F*YX^j#! z@?-eQU`OUw#&EJe?2K)n`nzp*%lC|Z!8z;K8xEIxnEL1qG2&5Wet158_$XW$--cVU zm|?bo^wQ(rwCh+mjBLX|#;U7ww9<7&%%7QS=;K~JtWrE-s`e@bp`aA5j@Iy}x{rNr z*Br3QMOS|$UtLd2ZF!@}wZBasIV;pOhxw^Jg5V)gP3E6fMu&{l? zXI}Cq(Q*;b`ST;=eux|Y`Cf?iJ=W4A@pAkdH=W3ui>3m@mYKh{3AYv04|Qb*d>#yF@{i?% zXl%ST5e_Jn0YC4jsQDU;yWF+Bnk3f;<^;jb5a)VRx6zt!wVR2~nqDLc^cPbql|}zji>FDL zx#pWl3*xd@F$l{f8FEXJ;?eZHy=}2<-1(RwI1zF`_HhnnuQfH8j9=A} z7S)z_hYHofd}YYb4|-$X6%TD?C(o`%!}X%;42@S<${du-d{`~8DijS5N1A_BAhc-{ zbcmucw^o%5ej9B7mb%Q6P27Jglo71~uxkLb=H|Nd<5>3Y#&S;ghJ1;C{)y~u_aLL$ z%0##3{HzBgo2vx(NU$6Dj`oI|81m{x7#7NX;}xc1i>02?@Pre??r^#OaJ9)9?_?~@ zB4Ah8-%m<$;G*JgnSeYoj3eUi2X!Au!}?@NGnUnckwKNXrJAJqa;;a_q|KK%bJ7Hr zy=jCg(afvGH@!+pvUtp@MLIZp*~ziJ|BiXK`1x~Fz2CtFWUCOUjFPn~UuVE(QrG0|n^nJ@mh%LnAIe|-Nh+#GIt)2Gw8Re z3dPgxv8v~b%-;`9u9}-su>GT8N{S9ueP}LPP0!mk*HWZm37jW;m#q0q3OjFF1{AMoD?uVS5&B2Ak-$YzLl(EwfISACd+tr8 zB=u4$SlL){&Rl`t??svdkG2M3K{Z)gi(J;+l1WUAqV_bHgx9FE4q>>4YS28(sqgOLi_ERL+|=8*zh%&?|i zen%WCoziz3s7_%#r<_z#-$KJava07?>BaV%y=e|ecGm55=^pG9WNhY}w#2+zQ#l{< zC22Zxk-vB4o&Gaz-86}b=b2X^6WJC?(_ZbPDHn$zN*!_RB+&?%Upw3&*`- zdb^x0ou; zWGbwe*u1^e!boJc$Y>H_sZEbLlKQM%a{ce7{+Fe+@wRCp!;)GkDO}40jSG}U^W46O z@#OwNaKr&*_%D5Y!?VYS*dd9Ucii{scK7%hT2uODecE*odIEd>7+7ufb({l=_nhH3}z%-o3vyLqp<)Sh3+%#l@d%?oHVH_JqBbN_%^z*kzyvo=oqo@Sg)?Ug*9mzc9W zT-0g%$jp~2r-*{SaOf2Hl-*txj|#+!Nkyq+=9*_)cqLiXP4m*h*~^ZO6_?b})M!}^ zYTDvpj<;h1Ov&jI``tyBG$p6~ZSttm+L>*bcU%x$0ThA`OOecn$#I)E*SqBdiC0kO zJAG|A`HYW?-G6_Y_lkp#nde)l7{v1bzYk(ppAPl`xz%4wmNLBUofTrEor$76^MCt{!@Q4W1Kbnbh|~>JkLB^O{C%`?syf0ur5V?T;FzBniPxX zH%-eIW(<|N7HN5ndm0Uo8XYJt?RCxUw2g%CLa)NW;sJ+YwZE?a@>+e+`Z|%zG1?;G%ltSvxdzr1M%l# zntTB_tI{e03oZw||>s!M>~YoMTn zcCPcEUcy_Z9CNoQKo0hs>H8l)kzkYKTKc3$$Di8!!MeT`nqoG=?S)ju*Q|~zX#7EG z%L-9EI85Fq%Q@R%jmYA!klhFDufh`1q!wgxV}n9GQNmn&Z+M)m#kTVkE$}9Q?@uu< z`N6^ZSe@@*%avYDm#{%Wzhj`ulhi8=Ogzd;CQ%kIZMO_fyDIo}4@*t)%rMF_WYS$T zT%>5yXvcOi4!uuPrlp~_M7kxBBq`nb4TIfHJ*&y)3KSQqvs8cE8>9A`2wN_K| zuz!#a-W=OIyYD2--nY89`((3QIpa&vwwIF_fUDT9a1SWATGkva>-YrJWo1@+-tNYWX1(*H6-xpyWqY2Fv3U+Cij2P`OgrSl|~~`uud8MbizheAAVT z!T9E~R;W#6)hG5LyQ5Z4zU@^~4FA@hvVv%Db*x=dbD;dwVVbj#&n3Ix*WWuwx}IJT z)A}&DV_4>!kx*mK+9INuWje?xa!cI^bM$`&lT+I_Mq z6Sq31mtT_=Nv9BF`P(IkBoXebbb?6g-THWV;ih=fB{HeQjFPbgUwV8H{1hme);T59 z`*eu*EcFe%`?Ul7XMF=7ug1L7uj44b7OoZ7k9;C@N~cHfpYspQZKr#jI=|B^YSZ1M zGG|poi&ksU3hGr0E3q8<0IO}Icl+CnVz$QXV!pn5A>#`yd%qo$^-C*GmuHPX)1KD& zKZyWc*o~Ely|w#o7@Uvm^wh}|I!lYTVVtZplPn2ZhARE-FB9cRe#_1B?^7h}amanv zOR&~g&8YiCgywUq&uQYeSQGI6bSiS0&iwT1>g^>bEw=L2dtQSAvgRk7q+HraVf?Ng0Vp6W2&>h;p>{>WD8 zGgp<4K{S2QpELs&0mUfWMTRjjPyb3L{0efnxKsw|4p4^?OlN%>*@W)VM1N5Se>ue3>Iq_A;>O=$H}H9s8x z1R9z;#Lr~#S$JtE8fNT0glgz#?zA))7kySXPp1cGCSR2&G5g95!{@`cw(JM_95=l= z*h;{{%Supt)t$`5bPU2gX)4=-Q#{hoo0e;vUZfl<(E;PZ;Q_KZ7o&ebFEdvf|}A$Tct9I=5ZVJ_)Vk*kwnt! zNVRZYWZ}tC)AROH2H_JasFXPE(mg-|0w36q3iZbCzW8WS1%!IHC;Dofd>FK=88jd%;C-0S&mcdpgGqd0VPTbK;cgk|gS{^wsvr8^X-)>rKnMy_-$=67H6ko$goAEZrr%Z?1& zHJv@M$=ZEAU6X7(FyftCvadc-{>Lyn*y*fI92p|CRl0d2N0y?Q;UVN!jq+JD&1ybb z)jQTJiYd-)<=^B=n+C`u+Hu;THHN>_!Tf!6)a2@*9-U(aagP;kYCt)WPd>7aiX2Wt=3&T)@PryIJOYnQhF|RNS-VSSF z2u7d|pMlTA9q<+S4dk$fJAo|Y5PT3WfD1vtl=Ut6HtbAG*aZtg8>y>MgHOZX!3(g% zUc?m+fZ5Ok126(bn1DOs%Wxn37wD6hGvO?_3_b$iffwPo(6tZw2eaW_FaSgF0k{{w z29JQgUVa-(fuk@6CD8KVqwsb3Ip`N-PlGezeefZ;9zF@r!>{3GcrD*2>8}Yv3k$8g`jS9)Nz}>z(jP_$#;pz5?o1 zz75vE&F~fYDtrf?gdaioe9{1?!x}gju7msFA$Sy?f;kJ&8F&+D7U<*f&+sL<8?+np zh=s%h-VOS}nGv`gZiTzxoA3gh(2E>+GpvBWfa~Eon15st90|+eWLN_=_!!&>cfmvO z82k@-8V>CvY*+xx;7{O6(Eh#~;ZArEo`c`OUj5_?Tm+ZErSNC)Bs>d0g5wtP9PWb$ z;BojaJO^_ZV@qHwTmqND$KfB~cKA>D4fHO-p29jPLJhWn_8{F355o^&)>774VHFI) zFl+>U9{D&t0f!xhuESEO!4|j#u7?}oXYdPn84f&}{DW0+7DTWGu7GRcI`|>XItG1% zlVK1(026Qldec%1X|s^8E%6w!@t8% zV8;_^t3W?YcqUv37lD?^{v5spUxy#SuVLrq=n<@dJX{Z-gwMjWu;Yo;PuLyy^bjL= zyvLvWnWpIq3h&6pb?7;mSI3EEyuGl}fPtX!A zaOr^#K1ejTYtYVjK(_oO#AIdFMEr!(d;#R|5=#Cm(7Uyq-v`<9c2npaBsdy##GO&G8wdc6C=bZ70a-epImoK-cYww&*fLYY|w#CxZ>ptN*4l{}$xA zbSTCbbp7o)G#;SqA~{;_sMfE;g$~rZu@A`NIeGx?2Pmk8oR0;GY>%fYYV&`sap57< zFp&RtKfCf&hkZCB$vV#G#fKr=*Y2^Sz^#oWvvDTtUUKPCklzbP+m#@>hf_#PeL;?D{b`;Wye zw?}9l%OFo{@O(B%V0)O9zB`iSdy`~U|Cs&g1ArtwLJmCbbK%>l$N%yw@(elfpFS6+ z6GA-QDTz9E=|O8j_hs_zLZ5H@Ql$>_)oLf_(%Wx~nm$gn1V2JlzTuN z@@13R`BzHfVP6`r;d~HC&Qnz4=Y2(P$~U`lKW1R@-)%1a5KxTXf$cb_qx~>d^FO?4 zNm%t7I>L~h1T&h+@}qhCEHV57WP9U!2_<75Bq9JCN5@m9{a zLyqe%C&}xM`$cY(q!!9j^*Q>`p9lG_lbi{V@AD5rBSF_!&`@3lx;_tOyTB{l8v2uG zf}XFSG*1Oxw}))$O7D#|ds8A>=YeAIV;sH+l9DHsF(1-{bLdHfo-ZTB6+YB=Q)ePy zZ^^2Nb%p|RUqi+8DtBx#w6}%nXFm# zt9JGMtZw}Fc>n7-4|bGgx=h02w#m|UD5WzGB>qX-sAnNNIv7%SG^}lEyQIbLavG5v zLH7?(z)$%?{sIlh*E%*HmWjl!r7>7QQ5^-6_wO_c--7JuS#CA2M;(k}g&_Z?z;RK< zqXAPLeYqvMI!+=NSAY&vaEoE1n9Zl=sh7o$gJa3n6F{L|go8^!Qh$y4dKq+mEeiSx z(Dmhr|EO2ewN%=7`HK4pQT>LmLhr^juJfjH)gDY#fc)OW`F?*rLQ7Z#J?}-&7J;t! zMNba&{qQ$&Ug@vz;Cz?AzMu1h{`yz@6IYPm*VD-feMjfdIsb*fzKQeS`0Ig#=rMuZ zk7HbF-oRXq5`Ek&(nFjd_17yytRI@YlU)!y=HI(>bs4*XJ(4zn@{lgMR36AV&7}-q22< z(ObP%f0Xmp{`zjtU-sAMQUlh5Zla?#o{BPC@Bbd&Mgs#OyW5&4m>5hkZfsIEN|%dOpE5-_*Bks@z?*z`3wH~3C>UX>z{G{g}?41Y#oiN zNu=Ge*8U$@)lqwRDV6hDP#yg^`SB@VN54x(JqJok4~~!aNgu`TZ2>*Mg!9$@`Y@8W z*e7vL4-?lQH^*^a<*(}`?_!_C$B$>y3FPMIod2)C{%ewPn@>tpSt)`#80yc8uY$Kx zuix(L_VMVC`*C01QDW{$TQ;+Y@z;+g;b7c_p!>(s(x<(qHic|jqQygdas96TyL>#Z z!|b>fVXhzL`Y~^Y?xC9B@2mPUs`l}|dYcRvJ&RZTt4zub9V|Xx=A0B?H#Q*>R literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..0670deb0a04d6a631f46d6081a5b81fae42bb26b GIT binary patch literal 2409 zcmXZd32cvN90%~XR2_59(XMqfSL>RiZi>}0YZlD4G2I*$bxkep;-02+Y%GjbW|SF* zh0al8Mulz|!>}4P8m08;(wRw@aSR9^sM-_VZDkKJ>*oEdi16rnOvig2#r=}JbTFF7?J)T zO>_Ftmld2PpP1r7FqbUWa*EQSK@iJ$wy~Y33@s4^8GJ|SuplTyPnNQR4QwWda};ou z+cYex547TS=CXl}M}C6622 z~pSioBLa++(De=Z2B5lL63a)4Y85oQ9*Fql-PvyOa9lna8Alp>1G3}g%` ze8z4b^Nf)4#=&b0WE4qEVWiXn_-6jM0B4;<$)uU3&Y zCNPo3oZuvPXjN6_>CXselgUbM@PH?jtri3osX;vNlf@>sl27Asb5A_INMbWv$mbHx zB7$HBIqc&&KXZxNFQ^YOB#_2-zGVm3D5OJV5G>>fN4Y=&H;IXoY1;87^Et#}ju27Z zywZl3>BAsCAe+-%pn#i1)Cht&l1U+z48Gtn=lPra{6kdDAb5*uY~=_~s9MWCYSWsw z#Ik^mY+)Y<_=!hUsBK*nOGkP$jb(gK9>=Ir$9+1}g^_&8MGCmg9YX7RmiD|tBC}Y= zHbUyj10Cr~9Nk&MGJdCceR(FFC>k+{c^u;m=XpfG7uBDIETVw02J%ZJHHl^{87yWg zYq&_shH5}%2D6gSS;av@8_72bWO0zgoF$*@)NO1G^d*5eSV1mFIl*t#X(BUpqbJMR z%^vo0nFi7N!BB>g!~(t|hg006Q;c@#P7gjMlR_Fd4T7e0CXNJBSi)*@ILc{$&fOAS18sh2+9%9D8?|B`JCfC^;^q2twcF&c~eKFIsf45734_%q5q{1RaB*3e^aw8yRG=nf?68O+q{QR#Ace n%wi66`Gh^3num~AdyT~@-Owi9Ds5r literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache new file mode 100644 index 0000000000000000000000000000000000000000..eee67165dfaf7e8ba96af84ef67f1d75e741dae4 GIT binary patch literal 7635 zcmd5>Nr+rU816Q1=|oTiMg_GDl0Dr<4`MWsUE`8K5>XE_UDL0+^D=Mg)T@_HYec-b zhiVeqbyyUf_?HUEf8n-qfFd&3UELD&UelS)QNkw^b4E zLc#(;JwD~1oI0ZJ^r#C@8Y%oFtIcj{L`MlPj+PiP5yc&0b}M1JZ6)HS=qgjKi2IcY z{0S9wMN65Cj(%e%*fez<3O_PR$8FK^&2F(-2Qq4{6tT906|r@DHb9)?)Fi&AL?_mP zSFKdUE@BeKUK%LTh$AEYC_w{Ln#3U1p&zw`GrASNQpm_7H7lY%;F6lBh_F7Kok?{x znVlGyjfn~^Ar^O#hqD#hrI0})YmKBT9*k40BM8_SVVoG)Bu&T?SQ-B43d{B*(T!6r z+B)8&8Yan>s)X7v8%F3fGJdFpR&A{kg;g6rj>zJ?(0wvURg%f`R9j)im|z#(h{LuY zWE4hf@(FYxV_x7Kf4-|X#Bso}K!JeRvxUcZ=~TI{Qi+MT$RO$W4v8*~a-!x)z0Wi^ z!u;(Rrev=9c^A6Q9V$r!!@BT{{BLe^y?r7-i+cGtnymL`tz*soG>75D&T;iNf50Y8 zQ7~MZ=SVJgbt8~T;y1hQr)GvLWv1&bk{(-+DdUuoVG2Ds2QCbZNHurHk%_ zlz7pyWn%4Gv1q5Y$RbNusg%l8IN0pQcPaPMN$weQ+Lsr&foipupI;64BtvOHLat$l zIS#!b&+{qt^pFk1e0nv9t!2ntBU^EA4tHnP;cklSNh5`t@DdI|)Mr3Bb{xb;t)x+i zId-$nHd!j?KqP-{k6bcGC?ZSSUb{UlA8wl3puirUd7Z8C^0N{ zN}a_?Bx!come-g+Y}l~V-!^j`tK6AFhRt`6wCgo+Sq(TxyGC>mm>qWIC2sAXrZZ>O zxsKJiZ^oq7a^OgtIylnA-@v>l$;2xh)3b%HZ-kXOJM7GfWrJ_l=PfaF#^zKs#n6KT zBaM6tPFYZakv}{)BUA2ONf+vUIA`s>!XEC(?rLvJ*;WR83$55&HogP$d7SPWTD8(Z zW_jGrfy;)U3MOp_3itj<+$HzPoek}`A#~v6zyU!v?s=ZiF=f9Q*aD>dn2EIA zWCF{aPX$h`Jx96*Tiu?#oW1UW$jJoLJ{EeCr_G6zLDavz=F=c`vn}khCVy3ai>vGj zp!_h0&-zJ+fA3R-f+fPo4C)cyrudKspMG`7u8(p$u5W4Nsiq$(ug;E#CQxLu!AZAi zn~FW!kbRHl&tmE?HWsVXf)0;ZJ%(O6Icdzq&4rXBB*>xpGGmsY?Zk}pmazSWVZ)Yq z7VA6&oa5;^i*9@z1+#o#b1rHIu^jNx;J`>HwU9&~1WP3#j^_QtbPbnx|r$1F;z*_g&xeB|St9+7~h2xWUz-W8Ic zv;2msh;Rqv-T>YP{sgu_$!CE>z+vDta21?GFV0n<286&~;3MEdIByxS3RnY7 z0140mUI*xzY9UhSmB2b+6QBY5O8p%80{9*H8`y|QxdTW6dTRIz_!>A3&^tvHpcM5q za1b~Q90862^mTR{a68}wA@Bk~pA=_+e}TowM$3T*06LG}1>OVZ;zoQcuoid%I0(E2 z&`tR|Y*hj51r7qw0N(+}0NTbYu#N8q?gh31`+)<%$H3PBU6)P*XMq0zilQn&H-je~ z&}+*>uCC!xw-zeTm$f~zTccOyw4Kxv-KZHAwrio(6CR$EZ>o)_e&E&Gm_RRa15ZVU zQu^tYNq@o6U%*gOiVZ5g3p@Qd4bPQFQDNMc-d8Kj`yO#}3>Yep2B_e&$}~T~uRj7q zX%J(!@FOJ?o&v=gU??FCP{C6I)7${%mW)ym8_5>BlQGfr(BMU2s35kYJ<*vef;SU@ zsMtgOIA9afzir0y!ft^n)={XV6b1=vNH0$;>o7Wx07EroBD!?5S@vK9MF9O?1&ONx zV)z^CKU|wvW))|`ZGGqciFyCHxiG6O12im{?te`G5^VW0K*BD7Y32gdo&!b~jS`F} z7+&!TNUH*tM<%yqm1KeR491h+Md$m#l*zo|viDvjWLqx9)GOTd53rg~0U|AQ2d~;4 zNBo|>7|iH@jI`$S!E2u5_X+Bwu6-1IzjVAGhbm72BUUflPrIedoDCPEzSgxXu;pdWo(oW4?b_F)wihVK`WbEeL5lWc2!w%F?- gjZ*1A+XGHxo?txyt!&|k940u9W&G-v;--}T2P(;l1^@s6 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..d7749a3ded03a2a09fb149cccd8909d90f6c4f8a GIT binary patch literal 475 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 Xc8F@S&!7#@SB*I7K*}8mO}qX-LjhQm literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..c3cd3085cd65daa623364a800da8dd7eaeab3d44 GIT binary patch literal 4725 zcmZA436ziJ8UXOmG7_R&LUs+Zg|St#j(w?1O7<~?h8bhVDEksITvur!BBHXJu|{Mc zDrFrL1~JA(i0aCjJG%NgIrkj*f6njx|L1$>`@Z*m-sgF~Z%R%J85rFABe-{^A)=|#4)Of)mV;_!9{M8%dQlo z6kQq5RAz9JLc(8|@>FIV2T7%@V0NV!(TpUKe{zu9gxK3hDMxi`Gn6?jVhPzaEf@qX zd5Nwhkw!X~xkYIS7Rq2gWHo#Eo;1?=H(9iOI0)vmh-9{r$z^K)EeJZ&jR@XgG>0hn zNDx${4&n4+0L%D>BmBTcuF~*P`%N+%*v(P3g@@=JE;4Ng||-^-_sm^d*iNr0_u5Ab6H0w4fjJxJKD>LGUCssY@cqIn5Qa z$n``JR3Myw#FD^NwsV+2C=n_qRHY_8c!xRMZGLy#+}&<=w{6|+PMcC_`DInaC8imD`l6A%AE@6PhxelboSeP4&TQ*0PJe93_94 zzDI2aFptk!#TqjBiJ!@)a4qqm7yTK?O7@elwj3mmc`Rlr%UQt&_L5G4I_6DJMl+3B zd_pqmoFjuQO4jwvVH9ui5#N%^HHy}AEv7SzkNK3PEN2C26nfg+i6DlzS;S5renuYA zlnEp}47d(0r>7{)>_ z^9wgA)j%E5mUc{I28-Fjaekq3Lu(+MnVcneBl%1ZR&bnCT;&FTP_D5YU?EFb!FGP8 zOcVW#9`xoPq;iV0)NJZp2Cd=vR64}TmHj}rRF{Uwt48oe*Bl-}}>r7+` zXSv7~?ohpjn&xv>l1w^9TbeVW)Tb3M6VH1r<7+YqY2~>@1QXfHJ`Qr3lCAAC?RcIS zn8QV`(727cvxV&($=%I*4HyBUsM{Zc+UOdr26BSj7R(5b}5L3pA%a{TWC! zG0f&8R*=M2vMAcYx(KBUU75`~_H&#piga{cUZM-#=*}b(Nu~UY#^}LHzGNE*IKq!S z-btO&h;R}Z#u_rn(#v>*;VfnezY+ejKEqT_aFHuybB6+*#gQoD7{)4A^E08Z z=NAR^B(t7h`HiQ$nj@`f%||RCiFNECe>c~mF3&QWG3+7e z9t1hbMJ-}k!&>f8pohLkS;`YeI4=^%6gH90c`j0>r$pNT1$wC|Ci6a@ zvX8GR9HB0#MgyAgJVRN`QBHD(>(uP69_T_3q8Q6;Ht{__Q8Cgpf;hI4uaCLXhfz%7 zT|Q(!3s}f%e&8Zm)a>gX%p{3#I8UBe&6{v$afB<}q)M^_TrXu2D@o=y!3giIbfy=5Sjsi7bA#F= zt%(V&VhuS)sY9l7f=Z*sn(<8H5D$)VZHm*LzAWG~mT{J%W7Q?0gwcj*hA@L94sxB_ z6SixRSa+XYrz3H9| zVHF$M${xNZ_eATUAzkQ61ihKTxBSEvicRvrG4c204F3AOoBOZtoH^eA->*Lbil>g| literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache new file mode 100644 index 0000000000000000000000000000000000000000..1de1e2ad08b4429c32b0ee13ec6c3a39c1886f28 GIT binary patch literal 25617 zcmeI43wT^roxtxvff~RHQi_0ZX>6O8WRm99v_jXm+d@led9*xQ+GH|wlT14^cRDj^ z8p`7Lp{$@hTtJ=+D4?K%qJY{}5f>I06j4C&!796ph%8hP5#d{4fB$pO|7PxFCJj)9 z{XTELU(T6x&-?t(`<%O{6FmJHFOhBT%lL_s80=MU++8fCGU;Ax&{RTuhxM&HHI z>{9W5RbkN6_s(usw0{FImX(>W6ju`FKdv%A%t;1vfmB@IYzl>b(} zsw+2^+Y(d#jaDPIxKQv5YA$vZ8|nRD z3ts3TkxRLKej(|(1%EJ?O6U6PBt8_%Z%JhH8L!B%o}M0)7j>kfNMiAEBs&{g?M?R}78A z9Xju5pVKwBCEniA*52OU=r*+(_Z{tBEiEna7VC!-;|YXEGG2>Ai&9mUC=*-#Ve(vU zj8fH>aiXHMbfg}P?Lb{e!%(Tp7yQ&53yAm75;`v%;MBbayDXdz+6QUUSRXiAZfepw~cG>z|`=r|1! z+iJaVPw+~Eg+5)GMBpXWA&}syXr`8n zZ%oC)0(~*X?+_*|1X$9U&I5lUZ#6E02aF1o!lg?+-h-Jdf(2zE<^@G-8!`jw(N2A*oZ*6qj|zo*w`XM`W z$xQOP69qCZ>y^^U`UT14U?GtlW<>AzX1r{1W}Q3i54zdJusc}v+_gOnUyEL5xZ6gw zXI(>mh0#nmohz2SM2h6TBl^;6L#?Qc_)@+q#-^|XapAxAWE+azW)h`BClXcpuA1bj z{rmxrb2jkAy>yQ;PJ>1^I-LL78lGBUt-J4u#b@jgG3lK!1xXPf-PvvrLwEqbG?p#)t6FSN#w znr`FLJ!5|8K%!+Fb%t)<5tUA5Mn!TA`P7+Q#_tcBB6TZrH2P%~fz!&-+Tl385Zj^x zS3b?lC73p_eiDY`HuNTF)V!H0Si!i!j^*qsO|Z_@6L*)I`CuJVl^-m*NEz!PBC2!{ zh?8miWw1eE>iDn6EM_&ZR5g=jo*hbB7_SEHu$ z6f+XzaX1PGBcohZEYWEBXu+yF4&hL%sz#jX>?9~7ZFbbEsu3sp<4rV?WlN4uIMpo< zW;GlMUz<}g9z#$UOmmzK5IfB}c26A4R+~>m#ReG~)gBO1!&=jZw91^JE#4N)89L)S zqO52S##ID{YJ;Y^)&5X%c+69D#Fy_(!u+oY*qU$ux1|~IYe{I(-q3c%bxMnh!7H&PI;mjct9mhM zFKFx}*3lPL@?D$0@9gh5KY^GIZQT=0#Wpo6+_#ZYXwKVGGF6Uc zo}mFhgGsP26WzkuVWl&1r2vBE;E~0~(UbK^BE~aKFkVZ_6wQ)yRejyLbxeIVGZhW4 zyd;7bW0b_*Qyk}Uh+S!&k5LlAj@77AV1CZzWjq5c6PuAjsM*G;y$;hFYBd>aK{QCG z7J!PG=7_4AU2nD!w6Q6ltEr!6eMF9P!-vRRrd_gV;xGio)eK zsT!=yGe6X!vl&VEda@o-d*Q9l)s-+=bEAhH|DK9TM$m$V!%j0i?x;PyEpwx@B%H_; zu4d5(PMF=vyeuexoV2#aJ6q?p&+eQ%ch20-xgD*owSB;QnV$t7`F-=)VF)H@G8ku) zR@-IZL{iu2w%EXTG_uyB(vnwI#p2D1n!0}j!{oPdFs?yo*<#K3ng^0M(Kl7AL9f}J z7^528dbs1f1hE&j8pxvIU`h6+s0TryQV(>*m`ZOphYQ5a%oY)KBkekuWRRv4({j7w zTQ&RPI5U`^8YepCWaYZ2rAM~y86t*jAUXq;#Y-bXrg>b2|FUjJ)Km6&f*F0x;w zqXZI%Vj|1Gbbn5wEe1U{$ni9?L3T9EW+Kw)_72aJ8Hr9tTjH~8i@$Z)RMjQ>Av<(A z;;^YKO!PZ;S{7);ZCQjXrnG;ncv8)@dWn3bylNFMc=P5eUVD?{+>6`HPNYa z)qR{GaR$IIO`nJ!;k zjgpay2IVg`Xw)kDHms3_`*~hEQ*nBy^3@mx{ng2ib3eq+2n%|w$(mX5e~YuVWDwzS zs=%7N9WjocujxvexEX7@R*_;UZpEf6qUZo@=MLa>XNN_@Q3uf~--Q*jtnz6?cJ4IC zxf>)qB-)x1eIH2*KO(8o9D5tq7RQX2Mlw3))%wHn8kXBlYVk!98hukOyVUl9a~!7$ zOn${;ngQi|^WygydxARkvr*9e7B4FwO~%fZ98GM2?Pou-=Qem-)KL2Tl{wm4 zIjt0oi3js36M*yzHi#l8{A8qU#-$ z0A=>jQ+~2Jo5(ebZ}EXrHd8;COLHh7+tf0DjOEuL@CJsl&CPDpe5QDv?VW8M9dlbd zyXJPbw02c(V#Jj;9X6P;NrG3Jx=r%{R|U`ZnI3Lo=A15 zVWWyXXjGozoY{Vd5VfyZf5P#d6qbf$XDW8J^G)^Q>QxZwhNjt^u?kkY(bQZEsBksA zg^7gpd~+|>p_W? zeEJA&Z`OG1o(i zt{JtMG>~Sy!W8V0k8t|*pl*~N5Wi@J~)aQ*X zux`@!pYt7Z)NLri4ld_7k^^3HqwD7gUk?14^BtUIE8@tR6u#DU60hMZMRws;ZX%GCBGNLeYM#IM`d_Xl7)|WYoJVEtw|pLv4N`eObqb&janJ;6G$s1gZS34Wd&$1sBZAm(RniwpTh8`Kc{?rxrm|Z_)q3!{ zvYSTiRmvq>yN^=7*Ydaimu(_*6Es@vJM4b7;~jOR?Xa;tnqAqChg(V&ld=Cd-K*A* zNHx~}t^8(pc)Bo=N~t5MRGLyT9-{|1qS6A?8PhwyxmJPWj^)UQ03_DY7ga>c z3Xt;<^VsbW`+=25cR{J-W%F_c-gk|^%!%y%+VZWJ^4U@OF=;o=mN`eQdCm;%znS41UAPY(fCu46u;1R;h11|uunPmY-Qh?`LO%?_ zRyYUlhlk*&us7=_^Fh{7)<7Q&!KH9LJOICk3GZ>7L!lm~!F1?_)8S0`I(%RsF7QJ) zoDJu~6>v3t0e%C|!t?MN%wznw5M(U44$^QsTmhej2jOXW2A+jkRNpwvhlQ{bd>8_` zm;PC}1MY^0;HU62_&H4G1n>J`CN#l3SOdKvCwxB%pN22Nx8b{RH{1gg+1T6*4h9#R zU^%RUQ{Z%v4bVs6CvX6#Ru6_oXn`YP4fH}EoC|Wg_1Ew?9KzYksn80Yun5*eA8ddj zxCpL~3qB4Pz*X=)co5`N#l#OFXK)}K z1joQi=!PWZ;X=3!WOe#ZxF3E155Uu~9bSdlG~@_6;7C{iS@=gd2V^7pUU(EHO{Y9z z8q9zONJ2kshST9=a2tFRkCUnADNJ2l9;Nx%sd>*UQF62r~CiR8w?e6hJ$NH)v3pMCMd#*{ylYi;fwOlMNfc`PLQy<+6U zHO9Ib*#<|t;zqAQIlEaMhcDw4eelq>nuqquJnj;EKR8&{8S)DoWv4~!O z40CX@+NdWBm*)Don#N9uq-jNBg?;BNP)n^kk<|mi|+G7B+hL@-;fMD0tB$9lQTM6vNh4IAiWg538HJurEPvlEynUvYk zDHVog!Wx|IGMdN{pz*p)Li_LUQg>QV9;LW#K% z=n+|VH>V3FK5#*qCE4p_gOeS`;Tu)exroiJaWvpj?r0C>>ejGVB$s!v?5Q-uR2{&0t z6y-B_+@0hDeQuCM3FuhO8A*T%kU714r6xQ}F9#n|;nxO?e@UN~foq!fI~PW5%EO+g zp6W4t$!Bi7lvT)5Vk6>Vc3Y0~bHj~nD{sOEAuab3xv=2E1e+~i`3gtT^Rn(B*V3f+ zOQS@-rwhdrXYJ)yy19Is^MkwTvNz`OFP&tKdRRm#oeF(47uQX`NL{2>1(B5>tk~eY zWP;7-N|}*{iHoov#p0#jqp2mQM?$3ejoBA_S~Y0#&8HX`L77LJdh=?6HC)BVD%H&m zi0(}werA%`j5^0T97KQGUW}7K^p~PuY4vgXj8++wWB$S<#swhySEevd0?}W_ zT;eJa{XE9X^Fj2Vq~cuyqSv?t@i&e29vI7Gzk;$$@XI-vkn3Sre(yuR8~`sr*b2MY%V?bV233GR2iQ%9*Yp;$z$N+8uh@(hu>JRgkPxbDrWaZawc7Bhn{DIBR zQ_0HpHajlBa}?ay@UAQ1iWWaUXVJ8vW_ zZ?2u4ni(vsYPuFNU|a$_$_#TVQ}h4*jBe^d9Nk86+iC~-Zmh@?-D=R#kEKss3pGL~ zN6b_H=}UFG6lq!wqn#Sb*QhSuq`D3Gma#s@$kSax9xbynu~Q?xMMo9YV1LPg?MWCN zY*b)_lg%3THhT8EKwO>WG93f)QH95Zt(I4A)Ec^GDzy&8>a0dqBS7?5A&6Il=x-(O zzgj#0Rhol?isC??bu$)FxLv5cl2hD|5_3M5b!v{frZ%1@runl6Cnq088xk{{b2&BjPG+Qoao#!eL z@2WU^7sm38eBUzo?M%kQL!gE`m6+h3mDh9ga{`D}r@`6f}g3u?I0bm=Fb z#F{)s8o8|?zjr~c1M%`u)EQPEO>d;eFAu-CdIPonbGF7u9hkbRMx2V#4?1cxUZylA zurwu6xSLY?p)INZLVeci7hz|GEwuz{#_HAEvie}-;Mi{unkxaC+AXY>d=w;w`=TBK z^6vTR%v?dtt9YrIr#B4LyY16hj016Y9_l3^?|jrDYhJ}Wx@_}kGi^Iu@kO4cygvoR zrz);@GnQDDz%=DTpF(VP$Mxaa={@<_24=hn43U6XrSEVw(K=DlfnAK z>?%#L@mNTkc@#)+`xERzHt3JhW}au;&C6*suddy0j^uA}ct>;Ms5TZwV02DIgz%_P z2=CmBN&xa`WKGP)2YLA1jmG_iSs9Iyssh7pQ#;h?Qf%psx_<^ zkm(p>3;F#u%JUwOlzoFf_+F?{KAIVNeoW*0GvfLVfEAsAm@nME{$^9p`^Q^rumVy|x^WBa$b9m`8%rA4}w* z*fQSA^D?2lW<#1v3}=A2yoZdq4@Cb9)L&cubEq#`{VS;E<2%@yfh0D9*zZLBpw)LH z2q`NJdDKCxzYz5jtM5fX`mK-@QMXwAMW~lq{ke3N7l8PGG3w=3e>JN4<}l_ZI_6xzp9w9;*46 z75eMZ-(<~iN8M)iccRLLF|q&HbjSHm5dCjapRxKTdYD!a^ZBTYto~@!W34_tlPkd> zcK#jdeZWfaFHs-2`e#s|xBA6M^)f5jC!n5e^-0tLt6$bkzJd6;3iVX0Uyqu!`dL)@ zW)M5=sB^7;KC1b|A?7Ex(HVi*S%=zd_5G;kdvaE2m%!Y)2Eo713jbCY;REp`;&4}MnWlbst9ACE zwE?l)>r%KN`WS^j0Yv`-nfoW3!5hfLtj);vBqm`K6bVN?4-PPB6n~5HvmjnJ(r&cc zwj<(H(i${J zW=@!TSI+cFnbU_i2qBjf6{E&R&Q{huSMUE(A#~DAW-vopLk;(vZzO5V;3akuV{AH8 z$Ynd1`G?lmg%HOxY^72C5XP{A)l_q@eGKEjJxyaeyQ$y^r#Q`J;u?m~fh4*!h6!X+ zLbN1KNpq%)C?{7wDFAw<)Zc-oOnIuWwT;{!Iag?iVA zkibahvXDX+vzz*I5ltK7>C6yDFq#Mzv}h`J3}iG@$)}J~_Hckhl=B0vn(50^ETdj? zaiA|5WRXV!b2&%_M`#u!zw{%G2zeAy!XeI5|Ar85BY{Mcm`Fae_?(U{)DYvD!#s*v z&4-+%hV#_Yr=@inz&Iw8PciE#=QP#S(B;Mux^h4L7)lO#6tIpm_ENzSe&JVIw+i8A zI+IKv(wInuH+Ywg>|hUj*++D1wLm=WNM;Bl7)?H(a+sfKbCbT@NjLhE#zZ2_V;6sL zoLghnHYud?FzH0dCXXeo?N+@Lur#Qm}u94bSKIlgQOL>oDG>s3TCG8o@0^Vd9%UQt=E^*T>A;dDA zkz_KN7g<9oAF-9=R8dXrt zZz$&{qC2WH;%Lhw3}rkSoSmq%RLKmDiZdJoa;xm?Zfm zk=yA+23fpFKCiQxFWJRen%-^=Vi?8*R`W5NDdRJ0iRl!=jij-Hw^`37c5skWoZ$ko zokMt%9C9gR8;!cSQ=~GB*({-m63RKs?^MzHju394JzaT~tYtl$xI&XV_2p?M z5h06WRYy8g!lQ7^=#l<%4yiuJIP?uNM{NQSjuwN@&!A& z$W@~55_h`MoBN26!!)K-#7cgoMK^IEk$g{Enh3)B7{CylqR%#!QNv3-)b z)2x?D326Ar>!p*e4!O%A@@-wI6e2 z8?SjIN(uZ2Hhclg+IN;)v*b=R9dE$YWxLileBq@kd4~m13CrVyq~9@?@4rwH!=3}b gwANX1UCg%Gu@i5P$-|F6-VIg1V8;h4?R+@(4@#SHCIA2c literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..5535ba4406a7d401fbb8e106c998d9555e4684e1 GIT binary patch literal 73 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�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 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..2a8b055ab387254c177abcd4bb5ba9ef9b28ab7a GIT binary patch literal 3419 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 F{tv~DFJS-x literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache new file mode 100644 index 0000000000000000000000000000000000000000..664454804dc863b2b81e95039074bb7dca8b335a GIT binary patch literal 15072 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 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..f73711e4d9509214218955e72e348acb4f34a857 GIT binary patch literal 1357 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 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..1777c3675dce52a84f1ffba9354f7027798e32ec GIT binary patch literal 629 zcmXxiJxCQ$6bJD8Qu7OPN*zS&S(~6JNFq)Sf;vRXa|}}QD-oxJhv*9g!G;vkMZpgW zB5M&8e3XNO$Ri#Kfr|u*gF=yuO9}oIJ@DfmE{Dq>&b@c~wl~~Vx9qcB4*qBIeslR@H)lHd&~(){FzzZ7MLP)0d1x)@=W z_k83Nt3FFUoH$WK+6aruGS+AWg@QMm1@8Yf&v-p?jUByAotcU9Uqv$2-;#z~iZPDN%~E1!EM z-?@+kI!(ej7Z>hQ(mgJ7MVoPy%ZJ2bwk&yhzqB`N#kEpKRt9w`%aTm$*+y`mz1zJn zhouRNM@@4>Ml5fm2T2^ber*4hmZoc)u7`Ag4w+5h4V(#G(fK%w4dp{kMaQHgJ<@Sb zVN8m6H9O5Ft*p?ceKkrWZBXj>`?m8YDPwe&JJ;4qt9yoe?KW*}P|un*o1SOC&JTe< z&NCHdMWe^=`!3To(J9``yn0arYP1E z8!g1$*mx@II8;}|c$bDMpgx=QNu_BN>VWd3Nnw7BD0AUOZ$CvxlWy(At+-8ZQB0?( zNAK9!TWR$;4$Ine#PRfGGIbGns-pF2SDuYr5wdD~#@W{OD{bN{{fhKvqEgnA6Wf&P z=huUW!|66M55e9`it_UHa2wkYe=XI)F%)OZQaoE_yA@?c9T`@g0*edQ z8(!71oaLrNo5Rr1K;deOOhVh5f+W^7HheVk9T?~!9H?9eO)>{@uJ&;anp~@3o%$G8 zali5P=FX!2F*Z@1U-@Vfp(W--+_lQWbeD*|$kwUUcv&)dx4~Jk0nUTVUqM4tfX z!3A&;Tmv71kHOdA8*l^s4LC&j4tfD>ff%IV4R90u1bzm;g5SWOU=@4U2fUBp1lPc) z;4|W@^m7bKIo=Wmb0D8N7RU{CF8G{*KS@(hIugzMg+gUrRt!Cs; zaDMw>QHVRMPYyO`tqpx!|wnO+{^w~j$y1kz>Eaq;{Il84xx literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..6c3cb8be3efe36410053967ebd97fad46720e820 GIT binary patch literal 369 zcmXxeu}Xqr6b9f=Gu*t0mY;}laA=SSNtcLd5E@$(Ow0{J^Jarn}@j{DtehDifB-rDa3RTYN p^TB{mzL^jubR@$Kbsl)+i9e#%5H`rN!#-s$xS~mqw*^i9GY2j$FjxQp literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache new file mode 100644 index 0000000000000000000000000000000000000000..e4f1dc497ce9b7c06f9620a9f2789c4d11a64511 GIT binary patch literal 19673 zcmd^`4VWEOd4TVdAIS165lgJWVWaIXth?C+e}IsLEQH7dB*X+QA?D8R&So!r_s(+f z-E5Yi1d%8ftwGX=pePY6wN#&nSOYdHl8U#f&^am&lSoqmru=;sQi8WLHu)Mv8wEHxF?f;2|- zxBAsx+#ii4q52!IR_e@R(JyMa#8GUq6Rdk)F4ka8k@7lov`un|b#tXvo|IyLni596 z)9#2$MGG0BPPanCCYy!R+2?ilmz?1~uiWPq9sY9yK{~mT;})E(X;#)*Onv1) zMOz$owc||%(uJ!cc=wbbq5gi4{T1gI)o2KXu=H2d43nk~|t29YSJbB*z zmP}>{8onI-&Nxeh42=j(Z#%=ea$m0CxK<)#cfPGt(Dkj3^r>^&X%3PQZ&gufvOp3Y zEtQlSVv{BWqiwM*2%T6b))ves6N34a3PvThQ3Jx<1J5|h&@42|uneu5>FtshAkh+U z(gdkP>Y?K@nJI5Yke{&`IA`V7cp|lYu2gpXUMsy)E1vRDu~2gSLVlzo=8}^ulyW_u zlU>}Otz(1*wQXKu##gHoh^WbJLTm)hZdqw$zM`YmnZ2?iH>s|)tnLA&%6|^6(6-tZ zhR!&Ia6>}SH&&5r#h#jBomLB#qh^^o)K)0(UOKXBs4!e~2epb4H|2GMc7?GT%;fpFlqI-Us4E$0fWO~f9r*JZAB zdWQ<#<(yw|a=j)zu)K|N(_4p1L>jJRzqiob;;eIKIv+CX&{1{pN7Gte5>-m1pIOl) zX}U~Ss!_HpV$L`e)RXP!Xkc_Oqr9Sew`F!9BqDO<%eEFurPUa($tJJ-t|*Kuin!Y2DkXJe3X?YIHKMw)1ZuA1D?3%1H3UNcZ_Z@4 zK_)S0>w8;f=#1HpFsAjy64VyJRS5xXs80a3dQ-DZ)+$_$k2!mUm5?5hPELDbjrcGnZL>m}#p~7Oyg=Nt|FpiY`^z9rl=4C9m7Z zYiGVndZSXBL1QupqcGVvXIV6Jr^_$*nZlWQ2M0a3DD!C1TjLc=p5qsL*rLj=s8JTx zgI;&pVJ@b6G-uE+8RMxFuV2iq&K2Bza{jJkY7E5^PLOt5GmC8N@3ISSnK&CANA_7W znpezfbvjnIIxCvGnpzze<>54%&|npx3SQlqcBL5{ji_!cL2Z@)u+=Qp@XJ-x%yLdb z)1FuXX*JU9H2YDR)SW|EA)TZK;tTmmuvx%sm9pX)h=851?VS zj-Ta&J#N{o{EW^!eO|stt)q+tvu2Z#Av!3l_^uJBn_(hLIM-QiHh;RXX9%&lFN@Bx zK9tlpP5f}B=<-SqUvx<*F*OevPzAqi%J6!0)R_?=rI*U?NU;4VxdZi_(9o&P(o?6% zc2Nwrbkixa;9n~{hPBR#PKVt!m~~;*okJy~it(?Wm&U2A3q@2S2I+VehlE9+nC{e8 z?^6@%JxA4B^0&e@J>NRExy^MwzX77L#{A5R5mk4 z<)kq>80>gxHJOFg95o;s&HK>e>kf%{LAAXe8G)vb(M)8Nd6x|3=nA%MGTZ4m&Z65{ zr`ONt{b8EP2+#KB-E!G0WLu>>*rJkpxqKZn5h@ee(3|nem^4GQrLHlWp23V}rlwYBZA&B*8&>PcHlEoku@-hR zT-=9=YSP@|fmQ8?WEVu0C7(HRNNr&y&L7QooS(2AOVu2t*$#~y47kewI&kYak6vD_ zyh?6???B|K$I|AcC;ssu`Kp$w`IlodnQ3pr)VyMiDVvDkJk!ip=I|jfDx78o!$XFD ztHYAcD-P#az}aIkCRa%kpDN8Dav2!;e3#qZAJ`{5agO*yPQjDOue;kTk=iW!Sn09i zV}<9*kw=iA)nQ0FS!aWe^423xo%8U5`eJ-r1jN4-p4$?D2gSF*aYtu;Qku{=780o_p2@j2#7 z*2p(&wt03c-`~ZondqZ4x6Z-FHiXCA2Ye-Kj}C(MY@n{Sm$X5g8p9=&+Bg=;p-r&6 zJQ)!OXk;u%jEqiIAj#hfI$YK2ODSJisFCqUpK)k;vivYhojUBgNVAt>ldsaWR;#{_ z@7H%h!(N+%bFNohZMI(=zn8K3*`;QimBrjg58m_ch*!@!x2>NTbz^c_^8}wnL!pf} znUOTs1U9t<>B65?Ew`o(V#>hvki&gYl;5nVopS84w%J>9B5vjp_Cadi-UV^)l{)w0ZfHMic)%)Dki0#*dgAgPh?DDC)*;xeN`Z<0s<+|AH zd$JiRMK&%yzB#_zpZ$y71=O>5F>{+N+b^5zM3k~QJdlfS*X8uVh)3ICb|rSvZ*$^i z7iT8?Z??1H{jR`7$lO`IDANUY>ty_Lq$r02l@e;0t8tYOL~g3tpz3kr_U!=fXTFrM zZ#S!b)9GG}g%}DUd?|sEXB@UKS>7#LUL-r?JrH?n9^Byb`?UaRNc=^bP19A*GCUuK zhHGs4vF{;@X{dL0HZ`-$JuBnUnk+XJ{r<=aly0H-=TlAXqv_cRbvUUm@#rKGy&@al zOdEa8NvjaNiA^^m9C^mR;q(9>mnU=Gpy7caMcp@?D(Azlqt~0l)1Zo>AOGE^pgFqQ znrRkG)o(M|*mlUvc}b8WaeqmNp>~XIZ0M*a*Kw40VM4YmRJM}0kc^9(_`f z_I^L5y7#C|=Iw7lsz0+WOx#;D-Bso4+IR)aIIUy-9Bp*u?m&Iwt=t!A`cp`_D9_$GV{ z?t=$l7aWU~a^P*y0VP-m@?CW&{2X3_BN$Rg!yDlk$Uzy_!VcI2d*Nkx6{cX$&WFE( zm2ffq18fC3>G}cu5?+U?95~E?cfm!Fg)ZoZO>jNj2Digqup9mpo`v5*3+E3V@J?6& zJ`97LO>BpqunS&;282HWCW4IQufZKKxiOPzg_*D(cEC>fIqZgCK@;186QB)Fg>`Tx zd6CQ+z;SqQgS~1}BpcCE)?}r|^3O)%N z;S2Dua36@lpMt@k2F)-Z&VYq*7JL!zfN#S0;XZf-4(1x{kuVh;cpt0)xgvWj{1f~e zJO;mjC*diWgDE-(mOvJI;1ak2w!l|lE9`u@l~1Bb$~un3mGGH_udTo0dzzlZ1HWsoCMsxns1DJ4StQG05G*Z@~}YNqGC8^A7S*03SBN^>7>94tK#Gcm|$_*Wg6{{$V!A z-$JZ~kHbyy9rzyH1K)>d;RR^m3fm+&8WzD4SOzZq4QvLv>h=Wu8YXcyzX|>VnxO^y z!H3J?O87L~3qJz6%6}MF`RBrXI2|^@9yr3GT;3{|!4mzH(4;R3Na4{5M9b5xD zVHfOyz3>uDX(De}0v~{jAO|0U@4{2?E0CT40Uv#q0TriLD5J)PF&T+UpR;bH)reRDUuvL2Lg4<&zq59Yzu z0{eNqG{lEKcBp*TJ=%@b`nvy6FybxVW&^)SYB7fj%HS}NuX^XT6?X}b0F(Z``$I_4Ae zgcs2?;bcbJkuX-m@FRvMyqgBx3)K>0(c>kWJIKu2=6RJO8yT>Y+dSNJt-nC{Wwttt za4!Iflhzm7gJx5ppQtiFW614>YV~4vHjh13*XDeLp1m5XC6jP@I0?}t^QlxPR7;|D zj;>5K?Q={*zlUmRwK%?AYm!H?xx1iR612uVv0J{H_-2gP*FX}p7BY{~Mjcy=`A!e`>nvi={_n?C^wzm2~5Du{nNn(H{ou!{@>8dmqEf` z7Z+s^e{Kf08pOW_cdhlSn4*Jo0&(O)>m|B88))v;Ag^LQs6Wh>hxyh#n@ILKkkmh- z1OMH2;w$9vU$)~Wk+Kma&M9=-Jlk<+<1V)T$LYW)Z6`jD`;zr@i;?x17ux1D=F9eaW8+#DTSu${Yrjy=nE?g@;+b~_HY(3yW{JM<3RZPq`9&OE|) z=rOo&w*HUOnOEBm)ebQoN?Xk%e|1YAm}e_3-ex;z7lDu4&Y=zFiAq80*e|S5Au9^grVzh}zPUevl(ulT9!}L4W{Etqg zA-%bL*(*lVO=V#)9purbo9@sb<`0#%l3$_^ZUz2j?ji8L8Xc(>wI{%mM4o%m*ax6m ze`qpmRW#XL^x;gXmP{oOIXKp&uQaiKoe?CJ%R$ii}imUcdPa9Lyw=cx_l7*KN%$dSjBY036>wn z^ID!9@qCcz8xg=}D}>Fc{^#vb|2FRTtp8E6c?=}}M7p;T#D6_Q`zAZQuf_eW_5UO8 zR_kAkwlB9sy@Wer{TpzvvHmBS2=;;Gc`2H{-kN}k4F5)J0uD!?r&<#*1GmNcPr^OL z`hSX6JPwl2?{G8LOdO6o)%y1#k{7I4Uc;SW#c~|o`Zn9Oe~r7*`p-jUmRVDh!}YCy z8+Q8J)@J_%_fhMA9+CXUisdMD=1m~=nT>n0^)JLd$NFR9qQA`}r=7^eJeQK%924{F zW&+KhuH&B&ybfK4vLYe> z*y8?J(==1l8Twk#HGODYwlBbsF;WrhJR$6;f-X)({ags=$ zz))y1afRef@$g!=7Ou%P;o^SDEcR$rbtS6!lVwOEjic_`Xke{`5>FmYsIKW8tz0q# zZH-12R~w|AWF!`5Ys!ceQ*X|iF6nA1M)F$pI7K1JRByFDkwKuj24+AvSK%H>Q-t>6 z|Dov+xvLHsjjL*8h^QhP!vttFPrit&;%VT4G6R}#MBJibNfRQ?(e@dS*27dzQmFo< z>5{xpKAJ%tdYimM*QqL~HBtptP23L?qGd=mHCuISnviHhZKEdCHin&~71UlASI`z| zLUFZGnu(EkShr+R_2#VUlCGBWhlnFh^bx(%1_fqBU$AD@x`#~;Vl-%-%wO_o{Qvm{ zN2Dgwi^-u9?FE&m5u5%vQ1(Id`#--#&_4*#(GdIV5$R&p995M47~fikWTzdcZq4x> zO(==w(S+)H5~!6ES6(Drqmjkc2C0Vc-`LiE(wxaS_0dx57~9&~WaUkb)x>K{wH5IJ H%>eyxt#Bp@ literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..e5481e53881d8888a82904882a16dc059088c4a1 GIT binary patch literal 1699 zcmXxjeN0zX7zgl!ibxtJXkn?5R)e&+*_%ZLLB?7-O3Hj8CB*_?SV?80Cd5eBY)L_m zVd)}sv{-+r89JA}X@q1k=BTg+f&NNSlR2$)^l78BeRl88eeOMH=YF5KKh_n5fe{ro zaC6bX^{Hj~dCPBHRZ zTt^n!TWMqupRt!d&Jr^ygyAG|3#p_rgXKKOi`3J|0Xpg7C=sR&4k42)HnNl5 zG;@#+5{HD4#MRu)N=jHw3x_#FTyzNWjAjf46tjvZx`}X(^TZAf;bG>GOCcYzolp6e zqx6#y6T&2JV?GOcl0{TgLp=@bqLbh052LKgE3wbZed?R3yhgyc~n+`$YMu!ww?u!BaLXyGUNiHVng63HT) z9P+57hIjaoQ=H>G83`dgLOw51$}5y}jFY5}c1~oILmpLZWHSvMqmQ!;86#Ib$V}!@ z%MSMNIX@6#*rg$iV>0(Jn|b8&1aHwx%4PDw-J~;<63SUa75n&(R^k)YkwGSJ(#6!V zAw0|Tyvl2App8EMp`YoOhwwCoEM+$b=;4|xJYyLvD59Dg>S7nnTG`rOVc z*0PDce8oQY)61VkjSnH3c(TYQhdkb31GRK$pqmJ1h@I%$Wb-)lDd1(k;0Px;&Cp39 TOdy>{$)}V})Ujni7#RNodg1mN literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache new file mode 100644 index 0000000000000000000000000000000000000000..2a7799339b842d28d97da899659f6552b8a3eb53 GIT binary patch literal 18974 zcmdU%3zS_|d4TT)1sNeoBA|(coq=S{gmEUBga9!>V-j>UVog9^Vshu+GnvcW$K*be zTrkw7l_m=jRw`LM6eSugAX=e-g|(2vvPFfGN(%^tM<7@fs-O!g)VANh&;HLn=bm#X z!AY{(o%PMx=bZiC|Ni%X_TD!KrF^%l;AM(^Rj($u2ZtZ7sMd0Y{NR4>**o3!jPi(A zN^^50f4bqIa3oF1J&Rm41)Jsm?$zn}LL^X1Uo7{F)It8!A(o32L7bFNnA{w!lQywz)Qhs&aGexgB=vAunq&DoCYNqI! zT4U7f?dUKAHe$XwTJVZqspjQOy_zqrvH@569g)jtYb#B$oU0eS9#buwe5vYHYGhZ+ znL@cdQXj3F;mjJZgS%CkO1?H+%x6t4Pd3@Iq`WR)Ymk{X_B&j`KU^*c4J}obCKIRc zTr{|SFJ@@bhuwfR(A^vv4OqH@j~E$2&CY9yMrp7;-tKe%OHwO*)cMj9e~uYSFL zawHr|p6WebE!2`qrCibH5=Ox*E}>7uvY{~(g8A{2hk;60vzxT#7Sm-~0WGo?Ni){x znuA`oPVI+8TC)+!XjU#XY@|Z1l?uI19qBUDZW+oKYF?rBrvvm zOfg@oi@-O6!BiX0)QGk!zs4(dn4x;fA9WS4RoPKCs@$zy=?}pU!vHN;YR2zrWSp0os*FM- zuxbf{?y=%&E<@!)PE(gUgrBFSo?2IZu@>DJ4p?5}OTE6A&Xjt$ZM1tPFu@&4<9ydO zk6BbM4kAMcXQ5!w;FUandbW&kiY703W=*EREHKzGS;B}&JF#sFB-4eL$qsjza>>pL zOmrkz@l2^)Gv!Lot1vO3`DOaBsWspF0?%vYS;j|o)XV0F(Em1CZ--f2wyCUTUXb}9 z$J`@Br{Wc)q~t7#+Q32aCFgpTG%0h9krNtU87fyZ#SE1h&XiVpb{H{)D~6`5GVj@G z?jvGBkf>QwDG+L`k(D@%7(gdAhYh{lHLJ%AmAW$7Y`s`7WNPIKI>An>-R2?_dTfq! z&&_%}rWi%D=Om9F6%Li4@rq}kjvgIOv)uy`6?rDE^_|{pWWCq8t@kRa)%=CQEG%In z&tu}NV|gugz82EA*utbz<|86$A_1kk7%5_-sJS8nCEB;W{>;+dwM?nDOZDkG86DrH zn$9<3x@NcB_v^h>(5b=xX#YuN6ju#<8E^j`rTrI9RQwSQr&2S}G<1T-moco7!<}NE z$2vtqh`XUPm3kJ^f91N#EJ9B(!khYmSWb-Gk~z{3;XllIwM@Q1sWP<=QynhX3puNZ2Bnnf9xUC_a%n8pP{67d%lhQ-B-@!8 zC7aG3(>a(a@lSl&VXsnYbfPhWX<3@Q%bE`Pa_?vbg zPbeyG(oNDa+FjCR;>Ig(pHXoO%d%u5=u5r-tOk~xLoQJ?!PEGx48Ibu}AM?l`(~^9rh3hR7b`3uZV{98bV=n9IJ=JdO<8CEv4&$ zkOAgGPP<49#jb7}*nS3~LBcW3`e^TI<7!Q`{^RYvquX>yR9UE2j+jI5b!DhFibW55 zA-f`%XCzj!F{Rd@Y5vpI^G7FqAjl9slGX($%~9f-$VwC4ofl9C5qmQtvlwJxg34_7^H7< zIhg@ni)l{HnEp-&itM-j@{BCh?8YeBv&NHZ0auT4&t}uKLnL7#c;IpvZ;uD*X+m@! zUmX5oo7O@yPI@2deU){mAhjiY?HLn(eZBMjtX)i|>vy`$T7+Mwyxmuqq8a_Q3mGrf zF$HBemnhr}8E%$VCEB7veWhxr&ldIBzmFCiKBiKm9jKl6r>C?`wtbn1zn(JU{h9#$BC*7oZy2l*fOl~hPVlCU#G8Icud!C8FIxDO? zheS+0eOSwkcp6KjA^bd|n-bPmHLRFEl7`Bf%~keRRJ@)(JeBG_0{MB>WtE_zBoU*_ zPsHtE0SH;jm$HSr{fG=Nv92A?XNT=FRGuYPjU{WHdr)76EIQw4Lw?+PskEA1RmIdp ze)MT{iIhllTC|lK9ojIiIf9wtPgO1_w@Q{KoG1uOX{WFTkkZ6b7_Yty64Xg;xff(A zk0Os{>DYeCO1V|6{5^zJY9{6TD8$6e)*&2IVv9>EyS9)ooRYTYvv;>d{1PRXDORIM zqxdnUa|GR-p}yLwc4^c$f(sLQ5pOR^M%={rrBZ`HliNjmZ9;u7Z^R`vMbXBYiv?Z>dno2zf=^ZY^@LCAd zbZu?f+H^WW^Cc|Xutiqd-3K%?YP)?VHR76eh)mOo6D*rV>^046PO!=IwKmGG6O=FO zeK}*Fxu+#KmZ0mYU`)54v2_A=2C+tz&ToAclMq!B*HNE;hV%#A6E=xTEU>zjcBWk> zT)ROebEzH+Xxej5O#DgKGd9pL8!re_ba#if+#b8>!$AYShDx#+n<@C4IkL_h*`|?A z9QH=pKpK0arp*WI?(LI0`E6aJ>kofq*qzOGUl`hk+VJDmj@_-Yr3tGRkBCCjoE@Z~ z{T)~=nv@7f{G~qer$Rbit2UE5t%UR;fq2Ar!QD|2U2(JP{dTL{O2eKozhXPu(LHVv zy5?%SY;F728Hxe*Aid{rY3q7i+&E)7R}$&1vT@!u*zmWzuz^nPpbfoSE7Oh)r=@+MzvHT38BY{6O8OZORZw^A(6#xnwUy$hp|=~TJ3lv zsk$8?IbmaWao8wn=*Cd%ISJH{Y>~JrX*a$b1F5$bxhx&n^;g&Lv_Y2?VZQ9ru9H34 zb!MjNcbl+?LhAy9onkmDabk<@E{!0qqm3aAB5u6BcD#y#B(6dwnF#t)?@!Rcyc0Lx z%!BlFO)|$`TFvtl_lppH6Zs|#TP6}k?{~Rcxq}U!nTT2Xzg$jcnOEem5x=}Q>_#{(5`h?)g2LQIB_X6S1Cu#9qoE~ zo+uMyI7lv$fZVsWf9A3AeF>x=b%ooFbxhy>5&dH)COKiPXYsssS#Ol1qNPTMeb(T> zOP!O`?&Mq4s@}zS`PfE~G=aZf*>16NAMQz?q+RbCF}Nglb<&4VEQRq-VL^gggqr?t zKniD#vQzo{F^CCAB#PX8f>3`haddJEpL&t>#Az%m)=2>+0l_*6kQAsToglo6W-q(W z$*#VJqu}3VEbYYP6C&}qkrAk=+r!gRY8`?)Bx9|tdV3Q){I5WIn=5d_5R6X!I_)Q& z4Ur)^l&`SVft}f5I38kcOW%Y(xR9FTpVFaom2b-^h zzk@sAPWUDK8s38cfrIIxZkP?n!-rutG(ZkG-UvU0yWnnk8g{@0^yozBgc&dg7QX8S=>wpN4bc0w}>nuo13=zk$198$1e6gDe$%km=xTI2X=? z&2SC;3MMl2jsV$jl<$vEfvmC*!X~%|WZC;);Fs_c{0a=)F2};@unY!a2&%9Fu7fRb zFFXP-z`GbVQ(*?kT4@$W;cww#cocTP3$PRB9fC~2BG?2^z*K&x(Fy%94;DcdZh%|i zE|A}H{0v@z|AcA$uA>J&2N%O%z)#_2_$^F+59vZL^uwue2AmBW;Ct{Rcm$pV`L)x3 zz&rWHQ!gxpOJD2L*1=Wq1Go=<4o`rb zHJHs=gB%ROD!3WG55Iuz@Cv*N{|$e}I>K@|4`f~87T6AlvO#(n$Y!YrUx&Nkad--z zhG$?Fr?2M1XJHU3um-*e55h0tIgnFYZ^9%_YW2cGSPj?1x8U3G8gyXXOn|A-34PFj zOW-RY2GCZx7q-K1Vaf+msiR;joC)VZ9*S@w+zNNWJ@7L)gtI({gPiMG30J@-_-l9o z9)zdhS$G9zp@dI_1uy`Y!IdBzjxWG_k7f+Qk+2Ht(149_8C(O;!|&lhL)*e6m<0on zgSGG__#Qk1&%;i55ssUNe87CT5H5zF!b|W9{1MWfv>Pme0ay(e!ocE}!YUAk*j@%w**kt$o!>wvXQ;vxtQp{b=4ONYc7ZHrTAyYVnAx*>XZPw)4}*unkC#z%)&;2q@tp^~ zZk-LVzq?8L?69x&x6m$J^P(|PUDo;eag>^MdsJrVnOHc{C$gh5-Gzz1k+7Qbs-_Kh zB@@F{ATCxxeGr`HO8Ws*265~LNl*g?&4P-yF0|3E@xF!&*2#~jHSqOlu&p5Xv6?Z{Tj#H0YXM)P!oLA2;15yK_dsHFO)prQ z&`f7jm-&#~7n-YoZd!BwHC1`lRp)Nv{)1}|%_NWk?GD<+p8D2I|B~u|84|lg+sUrp zYL-LH0wW+XE;m?>kXTU(AP(u#HkPArB1-b=Rvz9C688;e%C|uL6VQf-f%sJ({Az^1 zALOW+gb=qt<|_~vtGJee4KEFUH936^-GJOr?ehN0iG?3&Dq(q0blTwY$=b@8&dt&T^ zuJteDdId;2Pm%UBApSn8(GTK(9ASDA#J>oe=QI%i9NKywh(A_q&CKl|YB9FbmEQ%q zznm7^>{_jZdG|1owBo(3?GbIlrIceCh@;rnSqae_`%`jBQQSSW(EaUNNd;4Wn5_OE zV_Nc^GC(|q>Hh~tyu9j&7EFq{Yl*#=&sMkRtu^*s;A99Tx zYfLSY?EFe#akr4|4?q%%x0uF|Ut?<4Tk+iJZdTJ0br83a>t)X0KBW<_VSi&GF&-z_ zQ!Z$%uvEgj@PT$M&dZp1Z-A6yka?;EQlS@_dtL+a|Gi12ehlJ|C9Vm{xZjTe;&x)w zz66qJ6?t0QZX{}CS?|@zC!?Pif<%6eiRE>WoZB~q$cTgxH;2!Q6F^+7tfXpP($Azc zfyoAY7bJU?7c0Z^KpbX@7FAbHCGKzN`PU#`RIxO)qEb|i-JoCAyS}-CqJ7mBFJ4g{ zZT`fjB|U}uEp~N`RaG+-anlGFQ-v>p)I^hPS4FMu3?j?_LnCQyr+Ux2DlViJr-9@X zNkcQ1b#V3HN0t5&B$36`_@f~H3#f4k#6Jni$gA~SKkwph;K8LJ>1-m?D?$9S4+CPRWdjcfP8%#WJf%xa3LFYM*z6BMx)v39k zbNx5xUrMWe(zV=0TrYP1-!tm|0FurzwB`)gqH9o(4W}Y6=eo)Hzrl5j^LNsq(?Q~% zz;(X!4{%-T{GaA}j`JVDs6NPz>}gz&b^bYA=Q;mv4Bk830N%#+5$E5*b*J;sM&-

c?)K@V}==KKo`D+VCtJd5kO&Y$O6aQ-_x`GN!qLswZB8N~yo;1teEz5JMb>1@BZ2ZByu|`Od*50WV4Ca=^--QO1Xn99_29# zDWR6_yv5sm#n)WpI?)mlLlSqAK{iV$qKs8E@F@p4$VGnUFaBlNO+hf3Y23%dlv2S< ze8}fCa)>rMi5V0GaU?T|dzj69ma&?()bc8=42u*;ESb!qlycTl!&cs>k&}GSS$^Ug z;ZZ@5KnkhMAe~pJWe1<|83%|MY<;Bg6bo6#bG$$eTlj!OoT7y`I=O2|5G-IJi`Ys% zy~HWCQ9MW{bD2jDb=1?qH(cZry^I^GMtGcTa#%_k)ikh^CQj4MV8t6^5-H@eoPC_6 zg-*KZA#J#tU>0?>5{mJ^m`)nA$YmG%Y2+{`=;lwx$Eq)0q>`68&M#bM?9JxoZl;sL zlPsW`JsjaQXKCkG28|FyCRJ2ZLkm~=gQ&P57|Lj7vY6#mv5`7DNQ@7Hd%2&Pq*Kjq z_Hu+{oZ>1&Mp`#1Oec*RnmEca;uC^kB9nQ7`4mvZYPRt)NBNdk+L$&f2=Z7;0lV4D z8P3zk75?F&M02x<#eBqhVn+wT2;#_RJB|FnIofC^b&T)k8FE;`^Q@(rGhCpD>%@zKM(TKvCeCx2q+6W2gqX+E6jH((_S48=PB7dNoxm8D zu!%Ze<4c-3M+beRI)rDCP8J*3!W(om@-};rL<%UUf*LkZPZz%vHBN4cCzUMnSj9T_ z&`uXU{K%B?>VpSZLnX(#L?3?>mMm|~A&XKfSx-GX*+mzT6RejI6PP$47-;_i_W|^N literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache new file mode 100644 index 0000000000000000000000000000000000000000..39da9477d8831db5b690c1787075629d830017d3 GIT binary patch literal 41832 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 NITfniDy>C^{||snke&bl literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache new file mode 100644 index 0000000000000000000000000000000000000000..8f8e09cd3af35389082f907a971d56c695d9d14a GIT binary patch literal 14962 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 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..c89575aca3bdba1c4d536fff6aaa6b6048e56dd5 GIT binary patch literal 891 zcmXZbNk|ny7y#gT=5ue)Gs01#A0fB%rv_l)yW6GP?!=z71i`GL;0K#x_Cq{ zFBv8`HG~4zQA!Q9)Nze&1{fqe8p0N~QbY@aMiJ5Tsdf>@4v%4p{yJxnvh&biiTrHym6^NLYE6P*{r4w`A9pJ7IrV*PyYPaSu- z&l@5OLdYPGLP|J51qZpvAVa)ml#hI3xn^2P72OQ+oEIe6q^UM@hBo^6O^QZa#zxAi zriQ!p(NEfv5GpxO2VGp}0TcY>7k??q)vJ_pf_ud2rH|C5?npi{3aF%(dYZY)4Q>-> TlyN4=$qQi{6&y(A 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 literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_meta b/ui/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..dd57e64f7d3aba5f223ec2aaa24198c6c03bd586 GIT binary patch literal 3269 zcmZYBd2m(L836D@WLHSQgt98iW?2Qa$i7$z6$oK50YXZxgZLOoSfT`os56bASY;EH zT~a_u09#4|r4<|LG{_Q>fXD=hECQua8OtCUmj2T657RgEoA=&5?|$dJ?|k>WxA9w5 zCWgDON>%QDsB!mw!ld!fP3{m$%lNob2s@~G&%mU}^o*U3e!&CxjHFM=h@_|ObNt8u z8=IanEs{3jM#b@{5bE4>-v7qNWlS4CzLvo*WTspk*>=;~u2J{h{iU}Sv}P6uh!HG~ zL2RI6xfKhmSU|-@6|tV8E9q=v3x8n`W!$E|IXcjnvCLu)ukkv&*iA8qDWi2|u}LPK zMXYBB2RO%Nu5p7HNp4Cf`jEgFBFyF$=CGZcgsRp=XPzdWk$lK{wo$?r9<1iK#50M7 zEGLItwsMlV>LCp04d!!{RyMr{)5zc@=ChjZ?BNg>xy2B*Fo*q|q{aOqq_K`%^0-NV zm9dBoT%k*?5GFE(seDFsZ8;&8Y3w8_T1?u|mf38en6tEru};>JOCHzgqoO7;nS83& z4IznnEar1=ao>X>RHFtpX+vKUd66uNh=~oM7c*E*4(nLY4>XSpp(U;8%?Ki7lFdqf zPpB6{1BNq!Pq;+QpO}L>q_CbMV(W+S2oWZ-lOvQ7(;$Sw3@3?EjAj)@oaPEq4MP|} zHg9lEzp}jyu%8L*vnPgH4dR8oq2*0M95?TNBEj!+$5%nJn$s_7(*JL@+FlY z3ZV*v$z~a6sQ0jQ=uQt_U@d>-25p;$FpOkI@d4*3r}rc3g>~eT$G1H6sCAP`E(a;$ z2-TW}P?!F^$60DL7mJ}RX9b%nC#ppV)p>y0MDq;KlEJHdL>|Q)CkBwoOx97rO*%ZL2QY>dvU#6UqJL_?=tg&Dvw$O1Z|xX; z>BkJ-<`9+JsA1aCo~}gL&MtQIR|=`tHiSkblFgs^7tPzLL*C(YexzM{@yKF1`4n)S z0Uh)zve?C+`Hr~9LwJmCOyE`KGLIFkVJ$fnaDY-SQmLc!8O0Jxi0vd_ETM$h&c3sR z5{?k}gkDZ63s}ng?B`ozpVYhPNiSYx7MuBsV|+uapQ#O!SkE4gQOZe9(Y%X1k-}J} z@Eew~k$etvnn#~9FIjA)l+&D{epmTr8mlR!h`luErq;;dCH_V^Rl2+0n9Ou$Fq2uV zWE+JXr&$j@n3g2)YciS367u+rYjo;qFZnHnoZ}Mzq-QVZ@*Yb$PvhQlMKU>TWHVd& zgdb?qCxnM-N+N5>=LYTi>J{Yh6^FS_&wlEUcR5O>r~PNT(TfCzGmac`*-9CWe=a5y z_#Gef4JWwFRc_O-zaBsW3;B?3oaYV=2Z%)zM)Cpq>|iICh>CYz@Hicr%v4?{n?)St zD95Qi&^?zdULl7K6tIgTVt%0}XiOsSvXA}5Jfnsg%_g?+DTNeMb&xrzOFS==$xI43 z!70kPMeJbfravReWHEo|3iXEABT`w)D!$|>$0_9m)f4nqy3n0L3}q}EDdj8|h#qQA zl6irNyvZNfLlOU=O=1YoF`XH#<|N^&gRi BF8u%i literal 0 HcmV?d00001 diff --git a/ui/javascript/gleam_stdlib/dict.mjs b/ui/javascript/gleam_stdlib/dict.mjs new file mode 100644 index 0000000..3db3b5f --- /dev/null +++ b/ui/javascript/gleam_stdlib/dict.mjs @@ -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} 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); +} diff --git a/ui/javascript/gleam_stdlib/gleam.mjs b/ui/javascript/gleam_stdlib/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/ui/javascript/gleam_stdlib/gleam/bit_array.mjs b/ui/javascript/gleam_stdlib/gleam/bit_array.mjs new file mode 100644 index 0000000..f776446 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/bit_array.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(); + } + } + } + } + } + } +} diff --git a/ui/javascript/gleam_stdlib/gleam/bool.mjs b/ui/javascript/gleam_stdlib/gleam/bool.mjs new file mode 100644 index 0000000..4dbd86f --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/bool.mjs @@ -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(); + } +} diff --git a/ui/javascript/gleam_stdlib/gleam/bytes_tree.mjs b/ui/javascript/gleam_stdlib/gleam/bytes_tree.mjs new file mode 100644 index 0000000..c2cb9c9 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/bytes_tree.mjs @@ -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; }, + ); +} diff --git a/ui/javascript/gleam_stdlib/gleam/dict.mjs b/ui/javascript/gleam_stdlib/gleam/dict.mjs new file mode 100644 index 0000000..cdde2f3 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/dict.mjs @@ -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); +} diff --git a/ui/javascript/gleam_stdlib/gleam/dynamic.mjs b/ui/javascript/gleam_stdlib/gleam/dynamic.mjs new file mode 100644 index 0000000..875ec6c --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/dynamic.mjs @@ -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); +} diff --git a/ui/javascript/gleam_stdlib/gleam/dynamic/decode.mjs b/ui/javascript/gleam_stdlib/gleam/dynamic/decode.mjs new file mode 100644 index 0000000..6ba81d5 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/dynamic/decode.mjs @@ -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); + }, + ); +} diff --git a/ui/javascript/gleam_stdlib/gleam/float.mjs b/ui/javascript/gleam_stdlib/gleam/float.mjs new file mode 100644 index 0000000..9bb8e29 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/float.mjs @@ -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)); + } +} diff --git a/ui/javascript/gleam_stdlib/gleam/function.mjs b/ui/javascript/gleam_stdlib/gleam/function.mjs new file mode 100644 index 0000000..dffa927 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/function.mjs @@ -0,0 +1,6 @@ +/** + * Takes a single argument and always returns its input value. + */ +export function identity(x) { + return x; +} diff --git a/ui/javascript/gleam_stdlib/gleam/int.mjs b/ui/javascript/gleam_stdlib/gleam/int.mjs new file mode 100644 index 0000000..e619038 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/int.mjs @@ -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); +} diff --git a/ui/javascript/gleam_stdlib/gleam/io.mjs b/ui/javascript/gleam_stdlib/gleam/io.mjs new file mode 100644 index 0000000..ddab435 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/io.mjs @@ -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 }; diff --git a/ui/javascript/gleam_stdlib/gleam/list.mjs b/ui/javascript/gleam_stdlib/gleam/list.mjs new file mode 100644 index 0000000..c7ecc1c --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/list.mjs @@ -0,0 +1,3029 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + makeError, + divideFloat, + isEqual, +} from "../gleam.mjs"; +import * as $dict from "../gleam/dict.mjs"; +import * as $float from "../gleam/float.mjs"; +import * as $int from "../gleam/int.mjs"; +import * as $order from "../gleam/order.mjs"; + +const FILEPATH = "src/gleam/list.gleam"; + +export class Continue extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const ContinueOrStop$Continue = ($0) => new Continue($0); +export const ContinueOrStop$isContinue = (value) => value instanceof Continue; +export const ContinueOrStop$Continue$0 = (value) => value[0]; + +export class Stop extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const ContinueOrStop$Stop = ($0) => new Stop($0); +export const ContinueOrStop$isStop = (value) => value instanceof Stop; +export const ContinueOrStop$Stop$0 = (value) => value[0]; + +class Ascending extends $CustomType {} + +class Descending extends $CustomType {} + +const min_positive = 2.2250738585072014e-308; + +function length_loop(loop$list, loop$count) { + while (true) { + let list = loop$list; + let count = loop$count; + if (list instanceof $Empty) { + return count; + } else { + let list$1 = list.tail; + loop$list = list$1; + loop$count = count + 1; + } + } +} + +/** + * Counts the number of elements in a given list. + * + * This function has to traverse the list to determine the number of elements, + * so it runs in linear time. + * + * This function is natively implemented by the virtual machine and is highly + * optimised. + * + * ## Examples + * + * ```gleam + * assert length([]) == 0 + * ``` + * + * ```gleam + * assert length([1]) == 1 + * ``` + * + * ```gleam + * assert length([1, 2]) == 2 + * ``` + */ +export function length(list) { + return length_loop(list, 0); +} + +function count_loop(loop$list, loop$predicate, loop$acc) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + let acc = loop$acc; + if (list instanceof $Empty) { + return acc; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + loop$acc = acc + 1; + } else { + loop$list = rest$1; + loop$predicate = predicate; + loop$acc = acc; + } + } + } +} + +/** + * Counts the number of elements in a given list satisfying a given predicate. + * + * This function has to traverse the list to determine the number of elements, + * so it runs in linear time. + * + * ## Examples + * + * ```gleam + * assert count([], fn(a) { a > 0 }) == 0 + * ``` + * + * ```gleam + * assert count([1], fn(a) { a > 0 }) == 1 + * ``` + * + * ```gleam + * assert count([1, 2, 3], int.is_odd) == 2 + * ``` + */ +export function count(list, predicate) { + return count_loop(list, predicate, 0); +} + +/** + * Reverses a list and prepends it to another list. + * This function runs in linear time, proportional to the length of the list + * to prepend. + * + * @ignore + */ +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$1 = prefix.head; + let rest$1 = prefix.tail; + loop$prefix = rest$1; + loop$suffix = listPrepend(first$1, suffix); + } + } +} + +/** + * Creates a new list from a given list containing the same elements but in the + * opposite order. + * + * This function has to traverse the list to create the new reversed list, so + * it runs in linear time. + * + * This function is natively implemented by the virtual machine and is highly + * optimised. + * + * ## Examples + * + * ```gleam + * assert reverse([]) == [] + * ``` + * + * ```gleam + * assert reverse([1]) == [1] + * ``` + * + * ```gleam + * assert reverse([1, 2]) == [2, 1] + * ``` + */ +export function reverse(list) { + return reverse_and_prepend(list, toList([])); +} + +/** + * Determines whether or not the list is empty. + * + * This function runs in constant time. + * + * ## Examples + * + * ```gleam + * assert is_empty([]) + * ``` + * + * ```gleam + * assert !is_empty([1]) + * ``` + * + * ```gleam + * assert !is_empty([1, 1]) + * ``` + */ +export function is_empty(list) { + return isEqual(list, toList([])); +} + +/** + * Determines whether or not a given element exists within a given list. + * + * This function traverses the list to find the element, so it runs in linear + * time. + * + * ## Examples + * + * ```gleam + * assert !contains([], any: 0) + * ``` + * + * ```gleam + * assert [0] |> contains(any: 0) + * ``` + * + * ```gleam + * assert !contains([1], any: 0) + * ``` + * + * ```gleam + * assert !contains([1, 1], any: 0) + * ``` + * + * ```gleam + * assert [1, 0] |> contains(any: 0) + * ``` + */ +export function contains(loop$list, loop$elem) { + while (true) { + let list = loop$list; + let elem = loop$elem; + if (list instanceof $Empty) { + return false; + } else { + let first$1 = list.head; + if (isEqual(first$1, elem)) { + return true; + } else { + let rest$1 = list.tail; + loop$list = rest$1; + loop$elem = elem; + } + } + } +} + +/** + * Gets the first element from the start of the list, if there is one. + * + * ## Examples + * + * ```gleam + * assert first([]) == Error(Nil) + * ``` + * + * ```gleam + * assert first([0]) == Ok(0) + * ``` + * + * ```gleam + * assert first([1, 2]) == Ok(1) + * ``` + */ +export function first(list) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + return new Ok(first$1); + } +} + +/** + * Returns the list minus the first element. If the list is empty, `Error(Nil)` is + * returned. + * + * This function runs in constant time and does not make a copy of the list. + * + * ## Examples + * + * ```gleam + * assert rest([]) == Error(Nil) + * ``` + * + * ```gleam + * assert rest([0]) == Ok([]) + * ``` + * + * ```gleam + * assert rest([1, 2]) == Ok([2]) + * ``` + */ +export function rest(list) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let rest$1 = list.tail; + return new Ok(rest$1); + } +} + +/** + * Groups the elements from the given list by the given key function. + * + * Does not preserve the initial value order. + * + * ## Examples + * + * ```gleam + * import gleam/dict + * + * assert + * [Ok(3), Error("Wrong"), Ok(200), Ok(73)] + * |> group(by: fn(i) { + * case i { + * Ok(_) -> "Successful" + * Error(_) -> "Failed" + * } + * }) + * |> dict.to_list + * == [ + * #("Failed", [Error("Wrong")]), + * #("Successful", [Ok(73), Ok(200), Ok(3)]) + * ] + * ``` + * + * ```gleam + * import gleam/dict + * + * assert group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 }) + * |> dict.to_list + * == [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])] + * ``` + */ +export function group(list, key) { + return $dict.group(key, list); +} + +function filter_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let _block; + let $ = fun(first$1); + if ($) { + _block = listPrepend(first$1, acc); + } else { + _block = acc; + } + let new_acc = _block; + loop$list = rest$1; + loop$fun = fun; + loop$acc = new_acc; + } + } +} + +/** + * Returns a new list containing only the elements from the first list for + * which the given functions returns `True`. + * + * ## Examples + * + * ```gleam + * assert filter([2, 4, 6, 1], fn(x) { x > 2 }) == [4, 6] + * ``` + * + * ```gleam + * assert filter([2, 4, 6, 1], fn(x) { x > 6 }) == [] + * ``` + */ +export function filter(list, predicate) { + return filter_loop(list, predicate, toList([])); +} + +function filter_map_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let _block; + let $ = fun(first$1); + if ($ instanceof Ok) { + let first$2 = $[0]; + _block = listPrepend(first$2, acc); + } else { + _block = acc; + } + let new_acc = _block; + loop$list = rest$1; + loop$fun = fun; + loop$acc = new_acc; + } + } +} + +/** + * Returns a new list containing only the elements from the first list for + * which the given functions returns `Ok(_)`. + * + * ## Examples + * + * ```gleam + * assert filter_map([2, 4, 6, 1], Error) == [] + * ``` + * + * ```gleam + * assert filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) == [3, 5, 7, 2] + * ``` + */ +export function filter_map(list, fun) { + return filter_map_loop(list, fun, toList([])); +} + +function map_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$fun = fun; + loop$acc = listPrepend(fun(first$1), acc); + } + } +} + +/** + * Returns a new list containing the results of applying the supplied function to each element. + * + * ## Examples + * + * ```gleam + * assert map([2, 4, 6], fn(x) { x * 2 }) == [4, 8, 12] + * ``` + */ +export function map(list, fun) { + return map_loop(list, fun, toList([])); +} + +function map2_loop(loop$list1, loop$list2, loop$fun, loop$acc) { + while (true) { + let list1 = loop$list1; + let list2 = loop$list2; + let fun = loop$fun; + let acc = loop$acc; + if (list1 instanceof $Empty) { + return reverse(acc); + } else if (list2 instanceof $Empty) { + return reverse(acc); + } else { + let a = list1.head; + let as_ = list1.tail; + let b = list2.head; + let bs = list2.tail; + loop$list1 = as_; + loop$list2 = bs; + loop$fun = fun; + loop$acc = listPrepend(fun(a, b), acc); + } + } +} + +/** + * Combines two lists into a single list using the given function. + * + * If a list is longer than the other, the extra elements are dropped. + * + * ## Examples + * + * ```gleam + * assert map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y }) == [5, 7, 9] + * ``` + * + * ```gleam + * assert map2([1, 2], ["a", "b", "c"], fn(i, x) { #(i, x) }) + * == [#(1, "a"), #(2, "b")] + * ``` + */ +export function map2(list1, list2, fun) { + return map2_loop(list1, list2, fun, toList([])); +} + +function map_fold_loop(loop$list, loop$fun, loop$acc, loop$list_acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + let list_acc = loop$list_acc; + if (list instanceof $Empty) { + return [acc, reverse(list_acc)]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(acc, first$1); + let acc$1 = $[0]; + let first$2 = $[1]; + loop$list = rest$1; + loop$fun = fun; + loop$acc = acc$1; + loop$list_acc = listPrepend(first$2, list_acc); + } + } +} + +/** + * Similar to `map` but also lets you pass around an accumulated value. + * + * ## Examples + * + * ```gleam + * assert + * map_fold( + * over: [1, 2, 3], + * from: 100, + * with: fn(memo, i) { #(memo + i, i * 2) } + * ) + * == #(106, [2, 4, 6]) + * ``` + */ +export function map_fold(list, initial, fun) { + return map_fold_loop(list, fun, initial, toList([])); +} + +function index_map_loop(loop$list, loop$fun, loop$index, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let index = loop$index; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let acc$1 = listPrepend(fun(first$1, index), acc); + loop$list = rest$1; + loop$fun = fun; + loop$index = index + 1; + loop$acc = acc$1; + } + } +} + +/** + * Similar to `map`, but the supplied function will also be passed the index + * of the element being mapped as an additional argument. + * + * The index starts at 0, so the first element is 0, the second is 1, and so + * on. + * + * ## Examples + * + * ```gleam + * assert index_map(["a", "b"], fn(x, i) { #(i, x) }) == [#(0, "a"), #(1, "b")] + * ``` + */ +export function index_map(list, fun) { + return index_map_loop(list, fun, 0, toList([])); +} + +function try_map_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return new Ok(reverse(acc)); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(first$1); + if ($ instanceof Ok) { + let first$2 = $[0]; + loop$list = rest$1; + loop$fun = fun; + loop$acc = listPrepend(first$2, acc); + } else { + return $; + } + } + } +} + +/** + * Takes a function that returns a `Result` and applies it to each element in a + * given list in turn. + * + * If the function returns `Ok(new_value)` for all elements in the list then a + * list of the new values is returned. + * + * If the function returns `Error(reason)` for any of the elements then it is + * returned immediately. None of the elements in the list are processed after + * one returns an `Error`. + * + * ## Examples + * + * ```gleam + * assert try_map([1, 2, 3], fn(x) { Ok(x + 2) }) == Ok([3, 4, 5]) + * ``` + * + * ```gleam + * assert try_map([1, 2, 3], fn(_) { Error(0) }) == Error(0) + * ``` + * + * ```gleam + * assert try_map([[1], [2, 3]], first) == Ok([1, 2]) + * ``` + * + * ```gleam + * assert try_map([[1], [], [2]], first) == Error(Nil) + * ``` + */ +export function try_map(list, fun) { + return try_map_loop(list, fun, toList([])); +} + +/** + * Returns a list that is the given list with up to the given number of + * elements removed from the front of the list. + * + * If the list has less than the number of elements an empty list is + * returned. + * + * This function runs in linear time but does not copy the list. + * + * ## Examples + * + * ```gleam + * assert drop([1, 2, 3, 4], 2) == [3, 4] + * ``` + * + * ```gleam + * assert drop([1, 2, 3, 4], 9) == [] + * ``` + */ +export function drop(loop$list, loop$n) { + while (true) { + let list = loop$list; + let n = loop$n; + let $ = n <= 0; + if ($) { + return list; + } else { + if (list instanceof $Empty) { + return list; + } else { + let rest$1 = list.tail; + loop$list = rest$1; + loop$n = n - 1; + } + } + } +} + +function take_loop(loop$list, loop$n, loop$acc) { + while (true) { + let list = loop$list; + let n = loop$n; + let acc = loop$acc; + let $ = n <= 0; + if ($) { + return reverse(acc); + } else { + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$n = n - 1; + loop$acc = listPrepend(first$1, acc); + } + } + } +} + +/** + * Returns a list containing the first given number of elements from the given + * list. + * + * If the list has less than the number of elements then the full list is + * returned. + * + * This function runs in linear time. + * + * ## Examples + * + * ```gleam + * assert take([1, 2, 3, 4], 2) == [1, 2] + * ``` + * + * ```gleam + * assert take([1, 2, 3, 4], 9) == [1, 2, 3, 4] + * ``` + */ +export function take(list, n) { + return take_loop(list, n, toList([])); +} + +/** + * Returns a new empty list. + * + * ## Examples + * + * ```gleam + * assert new() == [] + * ``` + */ +export function new$() { + return toList([]); +} + +/** + * Returns the given item wrapped in a list. + * + * ## Examples + * + * ```gleam + * assert wrap(1) == [1] + * ``` + * + * ```gleam + * assert wrap(["a", "b", "c"]) == [["a", "b", "c"]] + * ``` + * + * ```gleam + * assert wrap([[]]) == [[[]]] + * ``` + */ +export function wrap(item) { + return toList([item]); +} + +function append_loop(loop$first, loop$second) { + while (true) { + let first = loop$first; + let second = loop$second; + if (first instanceof $Empty) { + return second; + } else { + let first$1 = first.head; + let rest$1 = first.tail; + loop$first = rest$1; + loop$second = listPrepend(first$1, second); + } + } +} + +/** + * Joins one list onto the end of another. + * + * This function runs in linear time, and it traverses and copies the first + * list. + * + * ## Examples + * + * ```gleam + * assert append([1, 2], [3]) == [1, 2, 3] + * ``` + */ +export function append(first, second) { + return append_loop(reverse(first), second); +} + +/** + * Prefixes an item to a list. This can also be done using the dedicated + * syntax instead. + * + * ```gleam + * let existing_list = [2, 3, 4] + * assert [1, ..existing_list] == [1, 2, 3, 4] + * ``` + * + * ```gleam + * let existing_list = [2, 3, 4] + * assert prepend(to: existing_list, this: 1) == [1, 2, 3, 4] + * ``` + */ +export function prepend(list, item) { + return listPrepend(item, list); +} + +function flatten_loop(loop$lists, loop$acc) { + while (true) { + let lists = loop$lists; + let acc = loop$acc; + if (lists instanceof $Empty) { + return reverse(acc); + } else { + let list = lists.head; + let further_lists = lists.tail; + loop$lists = further_lists; + loop$acc = reverse_and_prepend(list, acc); + } + } +} + +/** + * Joins a list of lists into a single list. + * + * This function traverses all elements twice on the JavaScript target. + * This function traverses all elements once on the Erlang target. + * + * ## Examples + * + * ```gleam + * assert flatten([[1], [2, 3], []]) == [1, 2, 3] + * ``` + */ +export function flatten(lists) { + return flatten_loop(lists, toList([])); +} + +/** + * Maps the list with the given function into a list of lists, and then flattens it. + * + * ## Examples + * + * ```gleam + * assert flat_map([2, 4, 6], fn(x) { [x, x + 1] }) == [2, 3, 4, 5, 6, 7] + * ``` + */ +export function flat_map(list, fun) { + return flatten(map(list, fun)); +} + +/** + * Reduces a list of elements into a single value by calling a given function + * on each element, going from left to right. + * + * `fold([1, 2, 3], 0, add)` is the equivalent of + * `add(add(add(0, 1), 2), 3)`. + * + * This function runs in linear time. + */ +export function fold(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return initial; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$initial = fun(initial, first$1); + loop$fun = fun; + } + } +} + +/** + * Reduces a list of elements into a single value by calling a given function + * on each element, going from right to left. + * + * `fold_right([1, 2, 3], 0, add)` is the equivalent of + * `add(add(add(0, 3), 2), 1)`. + * + * This function runs in linear time. + * + * Unlike `fold` this function is not tail recursive. Where possible use + * `fold` instead as it will use less memory. + */ +export function fold_right(list, initial, fun) { + if (list instanceof $Empty) { + return initial; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return fun(fold_right(rest$1, initial, fun), first$1); + } +} + +function index_fold_loop(loop$over, loop$acc, loop$with, loop$index) { + while (true) { + let over = loop$over; + let acc = loop$acc; + let with$ = loop$with; + let index = loop$index; + if (over instanceof $Empty) { + return acc; + } else { + let first$1 = over.head; + let rest$1 = over.tail; + loop$over = rest$1; + loop$acc = with$(acc, first$1, index); + loop$with = with$; + loop$index = index + 1; + } + } +} + +/** + * Like `fold` but the folding function also receives the index of the current element. + * + * ## Examples + * + * ```gleam + * assert ["a", "b", "c"] + * |> index_fold("", fn(acc, item, index) { + * acc <> int.to_string(index) <> ":" <> item <> " " + * }) + * == "0:a 1:b 2:c" + * ``` + * + * ```gleam + * assert [10, 20, 30] + * |> index_fold(0, fn(acc, item, index) { acc + item * index }) + * == 80 + * ``` + */ +export function index_fold(list, initial, fun) { + return index_fold_loop(list, initial, fun, 0); +} + +/** + * A variant of fold that might fail. + * + * The folding function should return `Result(accumulator, error)`. + * If the returned value is `Ok(accumulator)` try_fold will try the next value in the list. + * If the returned value is `Error(error)` try_fold will stop and return that error. + * + * ## Examples + * + * ```gleam + * assert [1, 2, 3, 4] + * |> try_fold(0, fn(acc, i) { + * case i < 3 { + * True -> Ok(acc + i) + * False -> Error(Nil) + * } + * }) + * == Error(Nil) + * ``` + */ +export function try_fold(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return new Ok(initial); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(initial, first$1); + if ($ instanceof Ok) { + let result = $[0]; + loop$list = rest$1; + loop$initial = result; + loop$fun = fun; + } else { + return $; + } + } + } +} + +/** + * A variant of fold that allows to stop folding earlier. + * + * The folding function should return `ContinueOrStop(accumulator)`. + * If the returned value is `Continue(accumulator)` fold_until will try the next value in the list. + * If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator. + * + * ## Examples + * + * ```gleam + * assert [1, 2, 3, 4] + * |> fold_until(0, fn(acc, i) { + * case i < 3 { + * True -> Continue(acc + i) + * False -> Stop(acc) + * } + * }) + * == 3 + * ``` + */ +export function fold_until(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return initial; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(initial, first$1); + if ($ instanceof Continue) { + let next_accumulator = $[0]; + loop$list = rest$1; + loop$initial = next_accumulator; + loop$fun = fun; + } else { + let b = $[0]; + return b; + } + } + } +} + +/** + * Finds the first element in a given list for which the given function returns + * `True`. + * + * Returns `Error(Nil)` if no such element is found. + * + * ## Examples + * + * ```gleam + * assert find([1, 2, 3], fn(x) { x > 2 }) == Ok(3) + * ``` + * + * ```gleam + * assert find([1, 2, 3], fn(x) { x > 4 }) == Error(Nil) + * ``` + * + * ```gleam + * assert find([], fn(_) { True }) == Error(Nil) + * ``` + */ +export function find(loop$list, loop$is_desired) { + while (true) { + let list = loop$list; + let is_desired = loop$is_desired; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = is_desired(first$1); + if ($) { + return new Ok(first$1); + } else { + loop$list = rest$1; + loop$is_desired = is_desired; + } + } + } +} + +/** + * Finds the first element in a given list for which the given function returns + * `Ok(new_value)`, then returns the wrapped `new_value`. + * + * Returns `Error(Nil)` if no such element is found. + * + * ## Examples + * + * ```gleam + * assert find_map([[], [2], [3]], first) == Ok(2) + * ``` + * + * ```gleam + * assert find_map([[], []], first) == Error(Nil) + * ``` + * + * ```gleam + * assert find_map([], first) == Error(Nil) + * ``` + */ +export function find_map(loop$list, loop$fun) { + while (true) { + let list = loop$list; + let fun = loop$fun; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(first$1); + if ($ instanceof Ok) { + return $; + } else { + loop$list = rest$1; + loop$fun = fun; + } + } + } +} + +/** + * Returns `True` if the given function returns `True` for all the elements in + * the given list. If the function returns `False` for any of the elements it + * immediately returns `False` without checking the rest of the list. + * + * ## Examples + * + * ```gleam + * assert all([], fn(x) { x > 3 }) + * ``` + * + * ```gleam + * assert all([4, 5], fn(x) { x > 3 }) + * ``` + * + * ```gleam + * assert !all([4, 3], fn(x) { x > 3 }) + * ``` + */ +export function all(loop$list, loop$predicate) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + if (list instanceof $Empty) { + return true; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + } else { + return $; + } + } + } +} + +/** + * Returns `True` if the given function returns `True` for any the elements in + * the given list. If the function returns `True` for any of the elements it + * immediately returns `True` without checking the rest of the list. + * + * ## Examples + * + * ```gleam + * assert !any([], fn(x) { x > 3 }) + * ``` + * + * ```gleam + * assert any([4, 5], fn(x) { x > 3 }) + * ``` + * + * ```gleam + * assert any([4, 3], fn(x) { x > 4 }) + * ``` + * + * ```gleam + * assert any([3, 4], fn(x) { x > 3 }) + * ``` + */ +export function any(loop$list, loop$predicate) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + if (list instanceof $Empty) { + return false; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + return $; + } else { + loop$list = rest$1; + loop$predicate = predicate; + } + } + } +} + +function zip_loop(loop$one, loop$other, loop$acc) { + while (true) { + let one = loop$one; + let other = loop$other; + let acc = loop$acc; + if (one instanceof $Empty) { + return reverse(acc); + } else if (other instanceof $Empty) { + return reverse(acc); + } else { + let first_one = one.head; + let rest_one = one.tail; + let first_other = other.head; + let rest_other = other.tail; + loop$one = rest_one; + loop$other = rest_other; + loop$acc = listPrepend([first_one, first_other], acc); + } + } +} + +/** + * Takes two lists and returns a single list of 2-element tuples. + * + * If one of the lists is longer than the other, the remaining elements from + * the longer list are not used. + * + * ## Examples + * + * ```gleam + * assert zip([], []) == [] + * ``` + * + * ```gleam + * assert zip([1, 2], [3]) == [#(1, 3)] + * ``` + * + * ```gleam + * assert zip([1], [3, 4]) == [#(1, 3)] + * ``` + * + * ```gleam + * assert zip([1, 2], [3, 4]) == [#(1, 3), #(2, 4)] + * ``` + */ +export function zip(list, other) { + return zip_loop(list, other, toList([])); +} + +function strict_zip_loop(loop$one, loop$other, loop$acc) { + while (true) { + let one = loop$one; + let other = loop$other; + let acc = loop$acc; + if (one instanceof $Empty) { + if (other instanceof $Empty) { + return new Ok(reverse(acc)); + } else { + return new Error(undefined); + } + } else if (other instanceof $Empty) { + return new Error(undefined); + } else { + let first_one = one.head; + let rest_one = one.tail; + let first_other = other.head; + let rest_other = other.tail; + loop$one = rest_one; + loop$other = rest_other; + loop$acc = listPrepend([first_one, first_other], acc); + } + } +} + +/** + * Takes two lists and returns a single list of 2-element tuples. + * + * If one of the lists is longer than the other, an `Error` is returned. + * + * ## Examples + * + * ```gleam + * assert strict_zip([], []) == Ok([]) + * ``` + * + * ```gleam + * assert strict_zip([1, 2], [3]) == Error(Nil) + * ``` + * + * ```gleam + * assert strict_zip([1], [3, 4]) == Error(Nil) + * ``` + * + * ```gleam + * assert strict_zip([1, 2], [3, 4]) == Ok([#(1, 3), #(2, 4)]) + * ``` + */ +export function strict_zip(list, other) { + return strict_zip_loop(list, other, toList([])); +} + +function unzip_loop(loop$input, loop$one, loop$other) { + while (true) { + let input = loop$input; + let one = loop$one; + let other = loop$other; + if (input instanceof $Empty) { + return [reverse(one), reverse(other)]; + } else { + let rest$1 = input.tail; + let first_one = input.head[0]; + let first_other = input.head[1]; + loop$input = rest$1; + loop$one = listPrepend(first_one, one); + loop$other = listPrepend(first_other, other); + } + } +} + +/** + * Takes a single list of 2-element tuples and returns two lists. + * + * ## Examples + * + * ```gleam + * assert unzip([#(1, 2), #(3, 4)]) == #([1, 3], [2, 4]) + * ``` + * + * ```gleam + * assert unzip([]) == #([], []) + * ``` + */ +export function unzip(input) { + return unzip_loop(input, toList([]), toList([])); +} + +function intersperse_loop(loop$list, loop$separator, loop$acc) { + while (true) { + let list = loop$list; + let separator = loop$separator; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$separator = separator; + loop$acc = listPrepend(first$1, listPrepend(separator, acc)); + } + } +} + +/** + * Inserts a given value between each existing element in a given list. + * + * This function runs in linear time and copies the list. + * + * ## Examples + * + * ```gleam + * assert intersperse([1, 1, 1], 2) == [1, 2, 1, 2, 1] + * ``` + * + * ```gleam + * assert intersperse([], 2) == [] + * ``` + */ +export function intersperse(list, elem) { + if (list instanceof $Empty) { + return list; + } else { + let $ = list.tail; + if ($ instanceof $Empty) { + return list; + } else { + let first$1 = list.head; + let rest$1 = $; + return intersperse_loop(rest$1, elem, toList([first$1])); + } + } +} + +function unique_loop(loop$list, loop$seen, loop$acc) { + while (true) { + let list = loop$list; + let seen = loop$seen; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = $dict.has_key(seen, first$1); + if ($) { + loop$list = rest$1; + loop$seen = seen; + loop$acc = acc; + } else { + loop$list = rest$1; + loop$seen = $dict.insert(seen, first$1, undefined); + loop$acc = listPrepend(first$1, acc); + } + } + } +} + +/** + * Removes any duplicate elements from a given list. + * + * This function returns in loglinear time. + * + * ## Examples + * + * ```gleam + * assert unique([1, 1, 1, 4, 7, 3, 3, 4]) == [1, 4, 7, 3] + * ``` + */ +export function unique(list) { + return unique_loop(list, $dict.new$(), toList([])); +} + +/** + * This is exactly the same as merge_ascendings but mirrored: it merges two + * lists sorted in descending order into a single list sorted in ascending + * order according to the given comparator function. + * + * This reversing of the sort order is not avoidable if we want to implement + * merge as a tail recursive function. We could reverse the accumulator before + * returning it but that would end up being less efficient; so the merging + * algorithm has to play around this. + * + * @ignore + */ +function merge_descendings(loop$list1, loop$list2, loop$compare, loop$acc) { + while (true) { + let list1 = loop$list1; + let list2 = loop$list2; + let compare = loop$compare; + let acc = loop$acc; + if (list1 instanceof $Empty) { + let list = list2; + return reverse_and_prepend(list, acc); + } else if (list2 instanceof $Empty) { + let list = list1; + return reverse_and_prepend(list, acc); + } else { + let first1 = list1.head; + let rest1 = list1.tail; + let first2 = list2.head; + let rest2 = list2.tail; + let $ = compare(first1, first2); + if ($ instanceof $order.Lt) { + loop$list1 = list1; + loop$list2 = rest2; + loop$compare = compare; + loop$acc = listPrepend(first2, acc); + } else if ($ instanceof $order.Eq) { + loop$list1 = rest1; + loop$list2 = list2; + loop$compare = compare; + loop$acc = listPrepend(first1, acc); + } else { + loop$list1 = rest1; + loop$list2 = list2; + loop$compare = compare; + loop$acc = listPrepend(first1, acc); + } + } + } +} + +/** + * This is the same as merge_ascending_pairs but flipped for descending lists. + * + * @ignore + */ +function merge_descending_pairs(loop$sequences, loop$compare, loop$acc) { + while (true) { + let sequences = loop$sequences; + let compare = loop$compare; + let acc = loop$acc; + if (sequences instanceof $Empty) { + return reverse(acc); + } else { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return reverse(listPrepend(reverse(sequence), acc)); + } else { + let descending1 = sequences.head; + let descending2 = $.head; + let rest$1 = $.tail; + let ascending = merge_descendings( + descending1, + descending2, + compare, + toList([]), + ); + loop$sequences = rest$1; + loop$compare = compare; + loop$acc = listPrepend(ascending, acc); + } + } + } +} + +/** + * Merges two lists sorted in ascending order into a single list sorted in + * descending order according to the given comparator function. + * + * This reversing of the sort order is not avoidable if we want to implement + * merge as a tail recursive function. We could reverse the accumulator before + * returning it but that would end up being less efficient; so the merging + * algorithm has to play around this. + * + * @ignore + */ +function merge_ascendings(loop$list1, loop$list2, loop$compare, loop$acc) { + while (true) { + let list1 = loop$list1; + let list2 = loop$list2; + let compare = loop$compare; + let acc = loop$acc; + if (list1 instanceof $Empty) { + let list = list2; + return reverse_and_prepend(list, acc); + } else if (list2 instanceof $Empty) { + let list = list1; + return reverse_and_prepend(list, acc); + } else { + let first1 = list1.head; + let rest1 = list1.tail; + let first2 = list2.head; + let rest2 = list2.tail; + let $ = compare(first1, first2); + if ($ instanceof $order.Lt) { + loop$list1 = rest1; + loop$list2 = list2; + loop$compare = compare; + loop$acc = listPrepend(first1, acc); + } else if ($ instanceof $order.Eq) { + loop$list1 = list1; + loop$list2 = rest2; + loop$compare = compare; + loop$acc = listPrepend(first2, acc); + } else { + loop$list1 = list1; + loop$list2 = rest2; + loop$compare = compare; + loop$acc = listPrepend(first2, acc); + } + } + } +} + +/** + * Given a list of ascending lists, it merges adjacent pairs into a single + * descending list, halving their number. + * It returns a list of the remaining descending lists. + * + * @ignore + */ +function merge_ascending_pairs(loop$sequences, loop$compare, loop$acc) { + while (true) { + let sequences = loop$sequences; + let compare = loop$compare; + let acc = loop$acc; + if (sequences instanceof $Empty) { + return reverse(acc); + } else { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return reverse(listPrepend(reverse(sequence), acc)); + } else { + let ascending1 = sequences.head; + let ascending2 = $.head; + let rest$1 = $.tail; + let descending = merge_ascendings( + ascending1, + ascending2, + compare, + toList([]), + ); + loop$sequences = rest$1; + loop$compare = compare; + loop$acc = listPrepend(descending, acc); + } + } + } +} + +/** + * Given some some sorted sequences (assumed to be sorted in `direction`) it + * merges them all together until we're left with just a list sorted in + * ascending order. + * + * @ignore + */ +function merge_all(loop$sequences, loop$direction, loop$compare) { + while (true) { + let sequences = loop$sequences; + let direction = loop$direction; + let compare = loop$compare; + if (sequences instanceof $Empty) { + return sequences; + } else if (direction instanceof Ascending) { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return sequence; + } else { + let sequences$1 = merge_ascending_pairs(sequences, compare, toList([])); + loop$sequences = sequences$1; + loop$direction = new Descending(); + loop$compare = compare; + } + } else { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return reverse(sequence); + } else { + let sequences$1 = merge_descending_pairs(sequences, compare, toList([])); + loop$sequences = sequences$1; + loop$direction = new Ascending(); + loop$compare = compare; + } + } + } +} + +/** + * Given a list it returns slices of it that are locally sorted in ascending + * order. + * + * Imagine you have this list: + * + * ``` + * [1, 2, 3, 2, 1, 0] + * ^^^^^^^ ^^^^^^^ This is a slice in descending order + * | + * | This is a slice that is sorted in ascending order + * ``` + * + * So the produced result will contain these two slices, each one sorted in + * ascending order: `[[1, 2, 3], [0, 1, 2]]`. + * + * - `growing` is an accumulator with the current slice being grown + * - `direction` is the growing direction of the slice being grown, it could + * either be ascending or strictly descending + * - `prev` is the previous element that needs to be added to the growing slice + * it is carried around to check whether we have to keep growing the current + * slice or not + * - `acc` is the accumulator containing the slices sorted in ascending order + * + * @ignore + */ +function sequences( + loop$list, + loop$compare, + loop$growing, + loop$direction, + loop$prev, + loop$acc +) { + while (true) { + let list = loop$list; + let compare = loop$compare; + let growing = loop$growing; + let direction = loop$direction; + let prev = loop$prev; + let acc = loop$acc; + let growing$1 = listPrepend(prev, growing); + if (list instanceof $Empty) { + if (direction instanceof Ascending) { + return listPrepend(reverse(growing$1), acc); + } else { + return listPrepend(growing$1, acc); + } + } else { + let new$1 = list.head; + let rest$1 = list.tail; + let $ = compare(prev, new$1); + if (direction instanceof Ascending) { + if ($ instanceof $order.Lt) { + loop$list = rest$1; + loop$compare = compare; + loop$growing = growing$1; + loop$direction = direction; + loop$prev = new$1; + loop$acc = acc; + } else if ($ instanceof $order.Eq) { + loop$list = rest$1; + loop$compare = compare; + loop$growing = growing$1; + loop$direction = direction; + loop$prev = new$1; + loop$acc = acc; + } else { + let _block; + if (direction instanceof Ascending) { + _block = listPrepend(reverse(growing$1), acc); + } else { + _block = listPrepend(growing$1, acc); + } + let acc$1 = _block; + if (rest$1 instanceof $Empty) { + return listPrepend(toList([new$1]), acc$1); + } else { + let next = rest$1.head; + let rest$2 = rest$1.tail; + let _block$1; + let $1 = compare(new$1, next); + if ($1 instanceof $order.Lt) { + _block$1 = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block$1 = new Ascending(); + } else { + _block$1 = new Descending(); + } + let direction$1 = _block$1; + loop$list = rest$2; + loop$compare = compare; + loop$growing = toList([new$1]); + loop$direction = direction$1; + loop$prev = next; + loop$acc = acc$1; + } + } + } else if ($ instanceof $order.Lt) { + let _block; + if (direction instanceof Ascending) { + _block = listPrepend(reverse(growing$1), acc); + } else { + _block = listPrepend(growing$1, acc); + } + let acc$1 = _block; + if (rest$1 instanceof $Empty) { + return listPrepend(toList([new$1]), acc$1); + } else { + let next = rest$1.head; + let rest$2 = rest$1.tail; + let _block$1; + let $1 = compare(new$1, next); + if ($1 instanceof $order.Lt) { + _block$1 = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block$1 = new Ascending(); + } else { + _block$1 = new Descending(); + } + let direction$1 = _block$1; + loop$list = rest$2; + loop$compare = compare; + loop$growing = toList([new$1]); + loop$direction = direction$1; + loop$prev = next; + loop$acc = acc$1; + } + } else if ($ instanceof $order.Eq) { + let _block; + if (direction instanceof Ascending) { + _block = listPrepend(reverse(growing$1), acc); + } else { + _block = listPrepend(growing$1, acc); + } + let acc$1 = _block; + if (rest$1 instanceof $Empty) { + return listPrepend(toList([new$1]), acc$1); + } else { + let next = rest$1.head; + let rest$2 = rest$1.tail; + let _block$1; + let $1 = compare(new$1, next); + if ($1 instanceof $order.Lt) { + _block$1 = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block$1 = new Ascending(); + } else { + _block$1 = new Descending(); + } + let direction$1 = _block$1; + loop$list = rest$2; + loop$compare = compare; + loop$growing = toList([new$1]); + loop$direction = direction$1; + loop$prev = next; + loop$acc = acc$1; + } + } else { + loop$list = rest$1; + loop$compare = compare; + loop$growing = growing$1; + loop$direction = direction; + loop$prev = new$1; + loop$acc = acc; + } + } + } +} + +/** + * Sorts from smallest to largest based upon the ordering specified by a given + * function. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * assert sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) == [1, 2, 3, 4, 4, 5, 6] + * ``` + */ +export function sort(list, compare) { + if (list instanceof $Empty) { + return list; + } else { + let $ = list.tail; + if ($ instanceof $Empty) { + return list; + } else { + let x = list.head; + let y = $.head; + let rest$1 = $.tail; + let _block; + let $1 = compare(x, y); + if ($1 instanceof $order.Lt) { + _block = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block = new Ascending(); + } else { + _block = new Descending(); + } + let direction = _block; + let sequences$1 = sequences( + rest$1, + compare, + toList([x]), + direction, + y, + toList([]), + ); + return merge_all(sequences$1, new Ascending(), compare); + } + } +} + +function repeat_loop(loop$item, loop$times, loop$acc) { + while (true) { + let item = loop$item; + let times = loop$times; + let acc = loop$acc; + let $ = times <= 0; + if ($) { + return acc; + } else { + loop$item = item; + loop$times = times - 1; + loop$acc = listPrepend(item, acc); + } + } +} + +/** + * Builds a list of a given value a given number of times. + * + * ## Examples + * + * ```gleam + * assert repeat("a", times: 0) == [] + * ``` + * + * ```gleam + * assert repeat("a", times: 5) == ["a", "a", "a", "a", "a"] + * ``` + */ +export function repeat(a, times) { + return repeat_loop(a, times, toList([])); +} + +function split_loop(loop$list, loop$n, loop$taken) { + while (true) { + let list = loop$list; + let n = loop$n; + let taken = loop$taken; + let $ = n <= 0; + if ($) { + return [reverse(taken), list]; + } else { + if (list instanceof $Empty) { + return [reverse(taken), toList([])]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$n = n - 1; + loop$taken = listPrepend(first$1, taken); + } + } + } +} + +/** + * Splits a list in two before the given index. + * + * If the list is not long enough to have the given index the before list will + * be the input list, and the after list will be empty. + * + * ## Examples + * + * ```gleam + * assert split([6, 7, 8, 9], 0) == #([], [6, 7, 8, 9]) + * ``` + * + * ```gleam + * assert split([6, 7, 8, 9], 2) == #([6, 7], [8, 9]) + * ``` + * + * ```gleam + * assert split([6, 7, 8, 9], 4) == #([6, 7, 8, 9], []) + * ``` + */ +export function split(list, index) { + return split_loop(list, index, toList([])); +} + +function split_while_loop(loop$list, loop$f, loop$acc) { + while (true) { + let list = loop$list; + let f = loop$f; + let acc = loop$acc; + if (list instanceof $Empty) { + return [reverse(acc), toList([])]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = f(first$1); + if ($) { + loop$list = rest$1; + loop$f = f; + loop$acc = listPrepend(first$1, acc); + } else { + return [reverse(acc), list]; + } + } + } +} + +/** + * Splits a list in two before the first element that a given function returns + * `False` for. + * + * If the function returns `True` for all elements the first list will be the + * input list, and the second list will be empty. + * + * ## Examples + * + * ```gleam + * assert split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) + * == #([1, 2, 3], [4, 5]) + * ``` + * + * ```gleam + * assert split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) + * == #([1, 2, 3, 4, 5], []) + * ``` + */ +export function split_while(list, predicate) { + return split_while_loop(list, predicate, toList([])); +} + +/** + * Given a list of 2-element tuples, finds the first tuple that has a given + * key as the first element and returns the second element. + * + * If no tuple is found with the given key then `Error(Nil)` is returned. + * + * This function may be useful for interacting with Erlang code where lists of + * tuples are common. + * + * ## Examples + * + * ```gleam + * assert key_find([#("a", 0), #("b", 1)], "a") == Ok(0) + * ``` + * + * ```gleam + * assert key_find([#("a", 0), #("b", 1)], "b") == Ok(1) + * ``` + * + * ```gleam + * assert key_find([#("a", 0), #("b", 1)], "c") == Error(Nil) + * ``` + */ +export function key_find(keyword_list, desired_key) { + return find_map( + keyword_list, + (keyword) => { + let key = keyword[0]; + let value = keyword[1]; + let $ = isEqual(key, desired_key); + if ($) { + return new Ok(value); + } else { + return new Error(undefined); + } + }, + ); +} + +/** + * Given a list of 2-element tuples, finds all tuples that have a given + * key as the first element and returns the second element. + * + * This function may be useful for interacting with Erlang code where lists of + * tuples are common. + * + * ## Examples + * + * ```gleam + * assert key_filter([#("a", 0), #("b", 1), #("a", 2)], "a") == [0, 2] + * ``` + * + * ```gleam + * assert key_filter([#("a", 0), #("b", 1)], "c") == [] + * ``` + */ +export function key_filter(keyword_list, desired_key) { + return filter_map( + keyword_list, + (keyword) => { + let key = keyword[0]; + let value = keyword[1]; + let $ = isEqual(key, desired_key); + if ($) { + return new Ok(value); + } else { + return new Error(undefined); + } + }, + ); +} + +function key_pop_loop(loop$list, loop$key, loop$checked) { + while (true) { + let list = loop$list; + let key = loop$key; + let checked = loop$checked; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let k = list.head[0]; + if (isEqual(k, key)) { + let rest$1 = list.tail; + let v = list.head[1]; + return new Ok([v, reverse_and_prepend(checked, rest$1)]); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$key = key; + loop$checked = listPrepend(first$1, checked); + } + } + } +} + +/** + * Given a list of 2-element tuples, finds the first tuple that has a given + * key as the first element. This function will return the second element + * of the found tuple and list with tuple removed. + * + * If no tuple is found with the given key then `Error(Nil)` is returned. + * + * ## Examples + * + * ```gleam + * assert key_pop([#("a", 0), #("b", 1)], "a") == Ok(#(0, [#("b", 1)])) + * ``` + * + * ```gleam + * assert key_pop([#("a", 0), #("b", 1)], "b") == Ok(#(1, [#("a", 0)])) + * ``` + * + * ```gleam + * assert key_pop([#("a", 0), #("b", 1)], "c") == Error(Nil) + * ``` + */ +export function key_pop(list, key) { + return key_pop_loop(list, key, toList([])); +} + +function key_set_loop(loop$list, loop$key, loop$value, loop$inspected) { + while (true) { + let list = loop$list; + let key = loop$key; + let value = loop$value; + let inspected = loop$inspected; + if (list instanceof $Empty) { + return reverse(listPrepend([key, value], inspected)); + } else { + let k = list.head[0]; + if (isEqual(k, key)) { + let rest$1 = list.tail; + return reverse_and_prepend(inspected, listPrepend([k, value], rest$1)); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$key = key; + loop$value = value; + loop$inspected = listPrepend(first$1, inspected); + } + } + } +} + +/** + * Given a list of 2-element tuples, inserts a key and value into the list. + * + * If there was already a tuple with the key then it is replaced, otherwise it + * is added to the end of the list. + * + * ## Examples + * + * ```gleam + * assert key_set([#(5, 0), #(4, 1)], 4, 100) == [#(5, 0), #(4, 100)] + * ``` + * + * ```gleam + * assert key_set([#(5, 0), #(4, 1)], 1, 100) == [#(5, 0), #(4, 1), #(1, 100)] + * ``` + */ +export function key_set(list, key, value) { + return key_set_loop(list, key, value, toList([])); +} + +/** + * Calls a function for each element in a list, discarding the return value. + * + * Useful for calling a side effect for every item of a list. + * + * ```gleam + * import gleam/io + * + * assert each(["1", "2", "3"], io.println) == Nil + * // 1 + * // 2 + * // 3 + * ``` + */ +export function each(loop$list, loop$f) { + while (true) { + let list = loop$list; + let f = loop$f; + if (list instanceof $Empty) { + return undefined; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + f(first$1); + loop$list = rest$1; + loop$f = f; + } + } +} + +/** + * Calls a `Result` returning function for each element in a list, discarding + * the return value. If the function returns `Error` then the iteration is + * stopped and the error is returned. + * + * Useful for calling a side effect for every item of a list. + * + * ## Examples + * + * ```gleam + * assert + * try_each( + * over: [1, 2, 3], + * with: function_that_might_fail, + * ) + * == Ok(Nil) + * ``` + */ +export function try_each(loop$list, loop$fun) { + while (true) { + let list = loop$list; + let fun = loop$fun; + if (list instanceof $Empty) { + return new Ok(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(first$1); + if ($ instanceof Ok) { + loop$list = rest$1; + loop$fun = fun; + } else { + return $; + } + } + } +} + +function partition_loop(loop$list, loop$categorise, loop$trues, loop$falses) { + while (true) { + let list = loop$list; + let categorise = loop$categorise; + let trues = loop$trues; + let falses = loop$falses; + if (list instanceof $Empty) { + return [reverse(trues), reverse(falses)]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = categorise(first$1); + if ($) { + loop$list = rest$1; + loop$categorise = categorise; + loop$trues = listPrepend(first$1, trues); + loop$falses = falses; + } else { + loop$list = rest$1; + loop$categorise = categorise; + loop$trues = trues; + loop$falses = listPrepend(first$1, falses); + } + } + } +} + +/** + * Partitions a list into a tuple/pair of lists + * by a given categorisation function. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * assert [1, 2, 3, 4, 5] |> partition(int.is_odd) == #([1, 3, 5], [2, 4]) + * ``` + */ +export function partition(list, categorise) { + return partition_loop(list, categorise, toList([]), toList([])); +} + +function permutation_prepend( + loop$el, + loop$permutations, + loop$list_1, + loop$list_2, + loop$acc +) { + while (true) { + let el = loop$el; + let permutations = loop$permutations; + let list_1 = loop$list_1; + let list_2 = loop$list_2; + let acc = loop$acc; + if (permutations instanceof $Empty) { + return permutation_zip(list_1, list_2, acc); + } else { + let head = permutations.head; + let tail = permutations.tail; + loop$el = el; + loop$permutations = tail; + loop$list_1 = list_1; + loop$list_2 = list_2; + loop$acc = listPrepend(listPrepend(el, head), acc); + } + } +} + +function permutation_zip(list, rest, acc) { + if (list instanceof $Empty) { + return reverse(acc); + } else { + let head = list.head; + let tail = list.tail; + return permutation_prepend( + head, + permutations(reverse_and_prepend(rest, tail)), + tail, + listPrepend(head, rest), + acc, + ); + } +} + +/** + * Returns all the permutations of a list. + * + * ## Examples + * + * ```gleam + * assert permutations([1, 2]) == [[1, 2], [2, 1]] + * ``` + */ +export function permutations(list) { + if (list instanceof $Empty) { + return toList([toList([])]); + } else { + let l = list; + return permutation_zip(l, toList([]), toList([])); + } +} + +function window_loop(loop$acc, loop$list, loop$n) { + while (true) { + let acc = loop$acc; + let list = loop$list; + let n = loop$n; + let window$1 = take(list, n); + let $ = length(window$1) === n; + if ($) { + loop$acc = listPrepend(window$1, acc); + loop$list = drop(list, 1); + loop$n = n; + } else { + return reverse(acc); + } + } +} + +/** + * Returns a list of sliding windows. + * + * ## Examples + * + * ```gleam + * assert window([1,2,3,4,5], 3) == [[1, 2, 3], [2, 3, 4], [3, 4, 5]] + * ``` + * + * ```gleam + * assert window([1, 2], 4) == [] + * ``` + */ +export function window(list, n) { + let $ = n <= 0; + if ($) { + return toList([]); + } else { + return window_loop(toList([]), list, n); + } +} + +/** + * Returns a list of tuples containing two contiguous elements. + * + * ## Examples + * + * ```gleam + * assert window_by_2([1,2,3,4]) == [#(1, 2), #(2, 3), #(3, 4)] + * ``` + * + * ```gleam + * assert window_by_2([1]) == [] + * ``` + */ +export function window_by_2(list) { + return zip(list, drop(list, 1)); +} + +/** + * Drops the first elements in a given list for which the predicate function returns `True`. + * + * ## Examples + * + * ```gleam + * assert drop_while([1, 2, 3, 4], fn (x) { x < 3 }) == [3, 4] + * ``` + */ +export function drop_while(loop$list, loop$predicate) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + if (list instanceof $Empty) { + return list; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + } else { + return listPrepend(first$1, rest$1); + } + } + } +} + +function take_while_loop(loop$list, loop$predicate, loop$acc) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + loop$acc = listPrepend(first$1, acc); + } else { + return reverse(acc); + } + } + } +} + +/** + * Takes the first elements in a given list for which the predicate function returns `True`. + * + * ## Examples + * + * ```gleam + * assert take_while([1, 2, 3, 2, 4], fn (x) { x < 3 }) == [1, 2] + * ``` + */ +export function take_while(list, predicate) { + return take_while_loop(list, predicate, toList([])); +} + +function chunk_loop( + loop$list, + loop$f, + loop$previous_key, + loop$current_chunk, + loop$acc +) { + while (true) { + let list = loop$list; + let f = loop$f; + let previous_key = loop$previous_key; + let current_chunk = loop$current_chunk; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(listPrepend(reverse(current_chunk), acc)); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let key = f(first$1); + let $ = isEqual(key, previous_key); + if ($) { + loop$list = rest$1; + loop$f = f; + loop$previous_key = key; + loop$current_chunk = listPrepend(first$1, current_chunk); + loop$acc = acc; + } else { + let new_acc = listPrepend(reverse(current_chunk), acc); + loop$list = rest$1; + loop$f = f; + loop$previous_key = key; + loop$current_chunk = toList([first$1]); + loop$acc = new_acc; + } + } + } +} + +/** + * Returns a list of chunks in which + * the return value of calling `f` on each element is the same. + * + * ## Examples + * + * ```gleam + * assert [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 }) + * == [[1], [2, 2], [3], [4, 4, 6], [7, 7]] + * ``` + */ +export function chunk(list, f) { + if (list instanceof $Empty) { + return list; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return chunk_loop(rest$1, f, f(first$1), toList([first$1]), toList([])); + } +} + +function sized_chunk_loop( + loop$list, + loop$count, + loop$left, + loop$current_chunk, + loop$acc +) { + while (true) { + let list = loop$list; + let count = loop$count; + let left = loop$left; + let current_chunk = loop$current_chunk; + let acc = loop$acc; + if (list instanceof $Empty) { + if (current_chunk instanceof $Empty) { + return reverse(acc); + } else { + let remaining = current_chunk; + return reverse(listPrepend(reverse(remaining), acc)); + } + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let chunk$1 = listPrepend(first$1, current_chunk); + let $ = left > 1; + if ($) { + loop$list = rest$1; + loop$count = count; + loop$left = left - 1; + loop$current_chunk = chunk$1; + loop$acc = acc; + } else { + loop$list = rest$1; + loop$count = count; + loop$left = count; + loop$current_chunk = toList([]); + loop$acc = listPrepend(reverse(chunk$1), acc); + } + } + } +} + +/** + * Returns a list of chunks containing `count` elements each. + * + * If the last chunk does not have `count` elements, it is instead + * a partial chunk, with less than `count` elements. + * + * For any `count` less than 1 this function behaves as if it was set to 1. + * + * ## Examples + * + * ```gleam + * assert [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2) + * == [[1, 2], [3, 4], [5, 6]] + * ``` + * + * ```gleam + * assert [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3) + * == [[1, 2, 3], [4, 5, 6], [7, 8]] + * ``` + */ +export function sized_chunk(list, count) { + return sized_chunk_loop(list, count, count, toList([]), toList([])); +} + +/** + * This function acts similar to fold, but does not take an initial state. + * Instead, it starts from the first element in the list + * and combines it with each subsequent element in turn using the given + * function. The function is called as `fun(accumulator, current_element)`. + * + * Returns `Ok` to indicate a successful run, and `Error` if called on an + * empty list. + * + * ## Examples + * + * ```gleam + * assert [] |> reduce(fn(acc, x) { acc + x }) == Error(Nil) + * ``` + * + * ```gleam + * assert [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x }) == Ok(15) + * ``` + */ +export function reduce(list, fun) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return new Ok(fold(rest$1, first$1, fun)); + } +} + +function scan_loop(loop$list, loop$accumulator, loop$accumulated, loop$fun) { + while (true) { + let list = loop$list; + let accumulator = loop$accumulator; + let accumulated = loop$accumulated; + let fun = loop$fun; + if (list instanceof $Empty) { + return reverse(accumulated); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let next = fun(accumulator, first$1); + loop$list = rest$1; + loop$accumulator = next; + loop$accumulated = listPrepend(next, accumulated); + loop$fun = fun; + } + } +} + +/** + * Similar to `fold`, but yields the state of the accumulator at each stage. + * + * ## Examples + * + * ```gleam + * assert scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i }) + * == [101, 103, 106] + * ``` + */ +export function scan(list, initial, fun) { + return scan_loop(list, initial, toList([]), fun); +} + +/** + * Returns the last element in the given list. + * + * Returns `Error(Nil)` if the list is empty. + * + * This function runs in linear time. + * + * ## Examples + * + * ```gleam + * assert last([]) == Error(Nil) + * ``` + * + * ```gleam + * assert last([1, 2, 3, 4, 5]) == Ok(5) + * ``` + */ +export function last(loop$list) { + while (true) { + let list = loop$list; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let $ = list.tail; + if ($ instanceof $Empty) { + let last$1 = list.head; + return new Ok(last$1); + } else { + let rest$1 = $; + loop$list = rest$1; + } + } + } +} + +/** + * Return unique combinations of elements in the list. + * + * ## Examples + * + * ```gleam + * assert combinations([1, 2, 3], 2) == [[1, 2], [1, 3], [2, 3]] + * ``` + * + * ```gleam + * assert combinations([1, 2, 3, 4], 3) + * == [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] + * ``` + */ +export function combinations(items, n) { + if (n === 0) { + return toList([toList([])]); + } else if (items instanceof $Empty) { + return items; + } else { + let first$1 = items.head; + let rest$1 = items.tail; + let _pipe = rest$1; + let _pipe$1 = combinations(_pipe, n - 1); + let _pipe$2 = map( + _pipe$1, + (combination) => { return listPrepend(first$1, combination); }, + ); + let _pipe$3 = reverse(_pipe$2); + return fold( + _pipe$3, + combinations(rest$1, n), + (acc, c) => { return listPrepend(c, acc); }, + ); + } +} + +function combination_pairs_loop(loop$items, loop$acc) { + while (true) { + let items = loop$items; + let acc = loop$acc; + if (items instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = items.head; + let rest$1 = items.tail; + let first_combinations = map( + rest$1, + (other) => { return [first$1, other]; }, + ); + let acc$1 = reverse_and_prepend(first_combinations, acc); + loop$items = rest$1; + loop$acc = acc$1; + } + } +} + +/** + * Return unique pair combinations of elements in the list. + * + * ## Examples + * + * ```gleam + * assert combination_pairs([1, 2, 3]) == [#(1, 2), #(1, 3), #(2, 3)] + * ``` + */ +export function combination_pairs(items) { + return combination_pairs_loop(items, toList([])); +} + +function take_firsts(loop$rows, loop$column, loop$remaining_rows) { + while (true) { + let rows = loop$rows; + let column = loop$column; + let remaining_rows = loop$remaining_rows; + if (rows instanceof $Empty) { + return [reverse(column), reverse(remaining_rows)]; + } else { + let $ = rows.head; + if ($ instanceof $Empty) { + let rest$1 = rows.tail; + loop$rows = rest$1; + loop$column = column; + loop$remaining_rows = remaining_rows; + } else { + let rest_rows = rows.tail; + let first$1 = $.head; + let remaining_row = $.tail; + let remaining_rows$1 = listPrepend(remaining_row, remaining_rows); + loop$rows = rest_rows; + loop$column = listPrepend(first$1, column); + loop$remaining_rows = remaining_rows$1; + } + } + } +} + +function transpose_loop(loop$rows, loop$columns) { + while (true) { + let rows = loop$rows; + let columns = loop$columns; + if (rows instanceof $Empty) { + return reverse(columns); + } else { + let $ = take_firsts(rows, toList([]), toList([])); + let column = $[0]; + let rest$1 = $[1]; + if (column instanceof $Empty) { + loop$rows = rest$1; + loop$columns = columns; + } else { + loop$rows = rest$1; + loop$columns = listPrepend(column, columns); + } + } + } +} + +/** + * Transpose rows and columns of the list of lists. + * + * Notice: This function is not tail recursive, + * and thus may exceed stack size if called, + * with large lists (on the JavaScript target). + * + * ## Examples + * + * ```gleam + * assert transpose([[1, 2, 3], [101, 102, 103]]) + * == [[1, 101], [2, 102], [3, 103]] + * ``` + */ +export function transpose(list_of_lists) { + return transpose_loop(list_of_lists, toList([])); +} + +/** + * Make a list alternating the elements from the given lists + * + * ## Examples + * + * ```gleam + * assert interleave([[1, 2], [101, 102], [201, 202]]) + * == [1, 101, 201, 2, 102, 202] + * ``` + */ +export function interleave(list) { + let _pipe = list; + let _pipe$1 = transpose(_pipe); + return flatten(_pipe$1); +} + +function shuffle_pair_unwrap_loop(loop$list, loop$acc) { + while (true) { + let list = loop$list; + let acc = loop$acc; + if (list instanceof $Empty) { + return acc; + } else { + let elem_pair = list.head; + let enumerable = list.tail; + loop$list = enumerable; + loop$acc = listPrepend(elem_pair[1], acc); + } + } +} + +function do_shuffle_by_pair_indexes(list_of_pairs) { + return sort( + list_of_pairs, + (a_pair, b_pair) => { return $float.compare(a_pair[0], b_pair[0]); }, + ); +} + +/** + * Takes a list, randomly sorts all items and returns the shuffled list. + * + * This function uses `float.random` to decide the order of the elements. + * + * ## Example + * + * ```gleam + * [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |> shuffle + * // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5] + * ``` + */ +export function shuffle(list) { + let _pipe = list; + let _pipe$1 = fold( + _pipe, + toList([]), + (acc, a) => { return listPrepend([$float.random(), a], acc); }, + ); + let _pipe$2 = do_shuffle_by_pair_indexes(_pipe$1); + return shuffle_pair_unwrap_loop(_pipe$2, toList([])); +} + +function max_loop(loop$list, loop$compare, loop$max) { + while (true) { + let list = loop$list; + let compare = loop$compare; + let max = loop$max; + if (list instanceof $Empty) { + return max; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = compare(first$1, max); + if ($ instanceof $order.Lt) { + loop$list = rest$1; + loop$compare = compare; + loop$max = max; + } else if ($ instanceof $order.Eq) { + loop$list = rest$1; + loop$compare = compare; + loop$max = max; + } else { + loop$list = rest$1; + loop$compare = compare; + loop$max = first$1; + } + } + } +} + +/** + * Takes a list and a comparator, and returns the maximum element in the list + * + * ## Examples + * + * ```gleam + * assert [1, 2, 3, 4, 5] |> list.max(int.compare) == Ok(5) + * ``` + * + * ```gleam + * assert ["a", "c", "b"] |> list.max(string.compare) == Ok("c") + * ``` + */ +export function max(list, compare) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return new Ok(max_loop(rest$1, compare, first$1)); + } +} + +function log_random() { + let $ = $float.logarithm($float.random() + min_positive); + let random; + if ($ instanceof Ok) { + random = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam/list", + 2244, + "log_random", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 55129, + end: 55200, + pattern_start: 55140, + pattern_end: 55150 + } + ) + } + return random; +} + +function sample_loop(loop$list, loop$reservoir, loop$n, loop$w) { + while (true) { + let list = loop$list; + let reservoir = loop$reservoir; + let n = loop$n; + let w = loop$w; + let _block; + { + let $ = $float.logarithm(1.0 - w); + let log; + if ($ instanceof Ok) { + log = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam/list", + 2227, + "sample_loop", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 54690, + end: 54736, + pattern_start: 54701, + pattern_end: 54708 + } + ) + } + _block = $float.round($float.floor(divideFloat(log_random(), log))); + } + let skip = _block; + let $ = drop(list, skip); + if ($ instanceof $Empty) { + return reservoir; + } else { + let first$1 = $.head; + let rest$1 = $.tail; + let reservoir$1 = $dict.insert(reservoir, $int.random(n), first$1); + let w$1 = w * $float.exponential( + divideFloat(log_random(), $int.to_float(n)), + ); + loop$list = rest$1; + loop$reservoir = reservoir$1; + loop$n = n; + loop$w = w$1; + } + } +} + +function build_reservoir_loop(loop$list, loop$size, loop$reservoir) { + while (true) { + let list = loop$list; + let size = loop$size; + let reservoir = loop$reservoir; + let reservoir_size = $dict.size(reservoir); + let $ = reservoir_size >= size; + if ($) { + return [reservoir, list]; + } else { + if (list instanceof $Empty) { + return [reservoir, toList([])]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let reservoir$1 = $dict.insert(reservoir, reservoir_size, first$1); + loop$list = rest$1; + loop$size = size; + loop$reservoir = reservoir$1; + } + } + } +} + +/** + * Builds the initial reservoir used by Algorithm L. + * This is a dictionary with keys ranging from `0` up to `n - 1` where each + * value is the corresponding element at that position in `list`. + * + * This also returns the remaining elements of `list` that didn't end up in + * the reservoir. + * + * @ignore + */ +function build_reservoir(list, n) { + return build_reservoir_loop(list, n, $dict.new$()); +} + +/** + * Returns a random sample of up to n elements from a list using reservoir + * sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L). + * Returns an empty list if the sample size is less than or equal to 0. + * + * Order is not random, only selection is. + * + * ## Examples + * + * ```gleam + * sample([1, 2, 3, 4, 5], 3) + * // -> [2, 4, 5] // A random sample of 3 items + * ``` + */ +export function sample(list, n) { + let $ = build_reservoir(list, n); + let reservoir = $[0]; + let rest$1 = $[1]; + let $1 = $dict.is_empty(reservoir); + if ($1) { + return toList([]); + } else { + let w = $float.exponential(divideFloat(log_random(), $int.to_float(n))); + return $dict.values(sample_loop(rest$1, reservoir, n, w)); + } +} diff --git a/ui/javascript/gleam_stdlib/gleam/option.mjs b/ui/javascript/gleam_stdlib/gleam/option.mjs new file mode 100644 index 0000000..45c88cb --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/option.mjs @@ -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([])); +} diff --git a/ui/javascript/gleam_stdlib/gleam/order.mjs b/ui/javascript/gleam_stdlib/gleam/order.mjs new file mode 100644 index 0000000..6324f45 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/order.mjs @@ -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; + } +} diff --git a/ui/javascript/gleam_stdlib/gleam/pair.mjs b/ui/javascript/gleam_stdlib/gleam/pair.mjs new file mode 100644 index 0000000..0964079 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/pair.mjs @@ -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]; +} diff --git a/ui/javascript/gleam_stdlib/gleam/result.mjs b/ui/javascript/gleam_stdlib/gleam/result.mjs new file mode 100644 index 0000000..21085d9 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/result.mjs @@ -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); + } +} diff --git a/ui/javascript/gleam_stdlib/gleam/set.mjs b/ui/javascript/gleam_stdlib/gleam/set.mjs new file mode 100644 index 0000000..6bec848 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/set.mjs @@ -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; + }, + ); +} diff --git a/ui/javascript/gleam_stdlib/gleam/string.mjs b/ui/javascript/gleam_stdlib/gleam/string.mjs new file mode 100644 index 0000000..434794d --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/string.mjs @@ -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 and + * 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); +} diff --git a/ui/javascript/gleam_stdlib/gleam/string_tree.mjs b/ui/javascript/gleam_stdlib/gleam/string_tree.mjs new file mode 100644 index 0000000..f9d78f4 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/string_tree.mjs @@ -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); +} diff --git a/ui/javascript/gleam_stdlib/gleam/uri.mjs b/ui/javascript/gleam_stdlib/gleam/uri.mjs new file mode 100644 index 0000000..1b1e18f --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam/uri.mjs @@ -0,0 +1,1147 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + isEqual, +} from "../gleam.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 * as $string from "../gleam/string.mjs"; +import * as $string_tree from "../gleam/string_tree.mjs"; +import { + pop_codeunit, + string_codeunit_slice as codeunit_slice, + parse_query, + percent_encode, + percent_decode, +} from "../gleam_stdlib.mjs"; + +export { parse_query, percent_decode, percent_encode }; + +export class Uri extends $CustomType { + constructor(scheme, userinfo, host, port, path, query, fragment) { + super(); + this.scheme = scheme; + this.userinfo = userinfo; + this.host = host; + this.port = port; + this.path = path; + this.query = query; + this.fragment = fragment; + } +} +export const Uri$Uri = (scheme, userinfo, host, port, path, query, fragment) => + new Uri(scheme, userinfo, host, port, path, query, fragment); +export const Uri$isUri = (value) => value instanceof Uri; +export const Uri$Uri$scheme = (value) => value.scheme; +export const Uri$Uri$0 = (value) => value.scheme; +export const Uri$Uri$userinfo = (value) => value.userinfo; +export const Uri$Uri$1 = (value) => value.userinfo; +export const Uri$Uri$host = (value) => value.host; +export const Uri$Uri$2 = (value) => value.host; +export const Uri$Uri$port = (value) => value.port; +export const Uri$Uri$3 = (value) => value.port; +export const Uri$Uri$path = (value) => value.path; +export const Uri$Uri$4 = (value) => value.path; +export const Uri$Uri$query = (value) => value.query; +export const Uri$Uri$5 = (value) => value.query; +export const Uri$Uri$fragment = (value) => value.fragment; +export const Uri$Uri$6 = (value) => value.fragment; + +/** + * Constant representing an empty URI, equivalent to "". + * + * ## Examples + * + * ```gleam + * assert Uri(..empty, scheme: Some("https"), host: Some("example.com")) + * == Uri( + * scheme: Some("https"), + * userinfo: None, + * host: Some("example.com"), + * port: None, + * path: "", + * query: None, + * fragment: None, + * ) + * ``` + */ +export const empty = /* @__PURE__ */ new Uri( + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), + "", + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), +); + +function parse_fragment(rest, pieces) { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + new Some(rest), + ), + ); +} + +function parse_query_with_question_mark_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string.charCodeAt(0) === 35) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else { + let rest = uri_string.slice(1); + let query = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + new Some(query), + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + new Some(original), + pieces.fragment, + ), + ); + } else { + let $ = pop_codeunit(uri_string); + let rest = $[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_query_with_question_mark(uri_string, pieces) { + return parse_query_with_question_mark_loop(uri_string, uri_string, pieces, 0); +} + +function parse_path_loop(loop$original, loop$uri_string, loop$pieces, loop$size) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + let $ = uri_string.charCodeAt(0); + if ($ === 63) { + let rest = uri_string.slice(1); + let path = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } else if ($ === 35) { + let rest = uri_string.slice(1); + let path = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + original, + pieces.query, + pieces.fragment, + ), + ); + } else { + let $1 = pop_codeunit(uri_string); + let rest = $1[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_path(uri_string, pieces) { + return parse_path_loop(uri_string, uri_string, pieces, 0); +} + +function parse_port_loop(loop$uri_string, loop$pieces, loop$port) { + while (true) { + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let port = loop$port; + let $ = uri_string.charCodeAt(0); + if ($ === 48) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10; + } else if ($ === 49) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 1; + } else if ($ === 50) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 2; + } else if ($ === 51) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 3; + } else if ($ === 52) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 4; + } else if ($ === 53) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 5; + } else if ($ === 54) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 6; + } else if ($ === 55) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 7; + } else if ($ === 56) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 8; + } else if ($ === 57) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 9; + } else if ($ === 63) { + let rest = uri_string.slice(1); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } else if ($ === 35) { + let rest = uri_string.slice(1); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } else if ($ === 47) { + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_path(uri_string, pieces$1); + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else { + return new Error(undefined); + } + } +} + +function parse_port(uri_string, pieces) { + let $ = uri_string.charCodeAt(0); + if (uri_string.startsWith(":0")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 0); + } else if (uri_string.startsWith(":1")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 1); + } else if (uri_string.startsWith(":2")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 2); + } else if (uri_string.startsWith(":3")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 3); + } else if (uri_string.startsWith(":4")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 4); + } else if (uri_string.startsWith(":5")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 5); + } else if (uri_string.startsWith(":6")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 6); + } else if (uri_string.startsWith(":7")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 7); + } else if (uri_string.startsWith(":8")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 8); + } else if (uri_string.startsWith(":9")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 9); + } else if (uri_string === ":") { + return new Ok(pieces); + } else if (uri_string === "") { + return new Ok(pieces); + } else if ($ === 63) { + let rest = uri_string.slice(1); + return parse_query_with_question_mark(rest, pieces); + } else if (uri_string.startsWith(":?")) { + let rest = uri_string.slice(2); + return parse_query_with_question_mark(rest, pieces); + } else if ($ === 35) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else if (uri_string.startsWith(":#")) { + let rest = uri_string.slice(2); + return parse_fragment(rest, pieces); + } else if ($ === 47) { + return parse_path(uri_string, pieces); + } else if ($ === 58) { + let rest = uri_string.slice(1); + if (rest.charCodeAt(0) === 47) { + return parse_path(rest, pieces); + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} + +function parse_host_outside_of_brackets_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + let $ = uri_string.charCodeAt(0); + if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(original), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else if ($ === 58) { + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_port(uri_string, pieces$1); + } else if ($ === 47) { + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_path(uri_string, pieces$1); + } else if ($ === 63) { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } else if ($ === 35) { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } else { + let $1 = pop_codeunit(uri_string); + let rest = $1[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_host_outside_of_brackets(uri_string, pieces) { + return parse_host_outside_of_brackets_loop(uri_string, uri_string, pieces, 0); +} + +function is_valid_host_within_brackets_char(char) { + return (((((48 >= char) && (char <= 57)) || ((65 >= char) && (char <= 90))) || ((97 >= char) && (char <= 122))) || (char === 58)) || (char === 46); +} + +function parse_host_within_brackets_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + let $ = uri_string.charCodeAt(0); + if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(uri_string), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else if ($ === 93) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_port(rest, pieces); + } else { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size + 1); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_port(rest, pieces$1); + } + } else if ($ === 47) { + if (size === 0) { + return parse_path(uri_string, pieces); + } else { + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_path(uri_string, pieces$1); + } + } else if ($ === 63) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_query_with_question_mark(rest, pieces); + } else { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } + } else if ($ === 35) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } + } else { + let $1 = pop_codeunit(uri_string); + let char = $1[0]; + let rest = $1[1]; + let $2 = is_valid_host_within_brackets_char(char); + if ($2) { + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } else { + return parse_host_outside_of_brackets_loop( + original, + original, + pieces, + 0, + ); + } + } + } +} + +function parse_host_within_brackets(uri_string, pieces) { + return parse_host_within_brackets_loop(uri_string, uri_string, pieces, 0); +} + +function parse_host(uri_string, pieces) { + let $ = uri_string.charCodeAt(0); + if ($ === 91) { + return parse_host_within_brackets(uri_string, pieces); + } else if ($ === 58) { + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(""), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_port(uri_string, pieces$1); + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(""), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else { + return parse_host_outside_of_brackets(uri_string, pieces); + } +} + +function parse_userinfo_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + let $ = uri_string.charCodeAt(0); + if ($ === 64) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_host(rest, pieces); + } else { + let rest = uri_string.slice(1); + let userinfo = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + new Some(userinfo), + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_host(rest, pieces$1); + } + } else if (uri_string === "") { + return parse_host(original, pieces); + } else if ($ === 47) { + return parse_host(original, pieces); + } else if ($ === 63) { + return parse_host(original, pieces); + } else if ($ === 35) { + return parse_host(original, pieces); + } else { + let $1 = pop_codeunit(uri_string); + let rest = $1[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_authority_pieces(string, pieces) { + return parse_userinfo_loop(string, string, pieces, 0); +} + +function parse_authority_with_slashes(uri_string, pieces) { + if (uri_string === "//") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(""), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else if (uri_string.startsWith("//")) { + let rest = uri_string.slice(2); + return parse_authority_pieces(rest, pieces); + } else { + return parse_path(uri_string, pieces); + } +} + +function parse_scheme_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + let $ = uri_string.charCodeAt(0); + if ($ === 47) { + if (size === 0) { + return parse_authority_with_slashes(uri_string, pieces); + } else { + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_authority_with_slashes(uri_string, pieces$1); + } + } else if ($ === 63) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_query_with_question_mark(rest, pieces); + } else { + let rest = uri_string.slice(1); + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } + } else if ($ === 35) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else { + let rest = uri_string.slice(1); + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } + } else if ($ === 58) { + if (size === 0) { + return new Error(undefined); + } else { + let rest = uri_string.slice(1); + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_authority_with_slashes(rest, pieces$1); + } + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + original, + pieces.query, + pieces.fragment, + ), + ); + } else { + let $1 = pop_codeunit(uri_string); + let rest = $1[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +/** + * Parses a compliant URI string into the `Uri` type. + * If the string is not a valid URI string then an error is returned. + * + * The opposite operation is `uri.to_string`. + * + * ## Examples + * + * ```gleam + * assert parse("https://example.com:1234/a/b?query=true#fragment") + * == Ok( + * Uri( + * scheme: Some("https"), + * userinfo: None, + * host: Some("example.com"), + * port: Some(1234), + * path: "/a/b", + * query: Some("query=true"), + * fragment: Some("fragment") + * ) + * ) + * ``` + */ +export function parse(uri_string) { + return parse_scheme_loop(uri_string, uri_string, empty, 0); +} + +function percent_encode_query(part) { + let _pipe = percent_encode(part); + return $string.replace(_pipe, "+", "%2B"); +} + +function query_pair(pair) { + let _pipe = toList([ + percent_encode_query(pair[0]), + "=", + percent_encode_query(pair[1]), + ]); + return $string_tree.from_strings(_pipe); +} + +/** + * Encodes a list of key value pairs as a URI query string. + * + * The opposite operation is `uri.parse_query`. + * + * ## Examples + * + * ```gleam + * assert query_to_string([#("a", "1"), #("b", "2")]) == "a=1&b=2" + * ``` + */ +export function query_to_string(query) { + let _pipe = query; + let _pipe$1 = $list.map(_pipe, query_pair); + let _pipe$2 = $list.intersperse(_pipe$1, $string_tree.from_string("&")); + let _pipe$3 = $string_tree.concat(_pipe$2); + return $string_tree.to_string(_pipe$3); +} + +function remove_dot_segments_loop(loop$input, loop$accumulator) { + while (true) { + let input = loop$input; + let accumulator = loop$accumulator; + if (input instanceof $Empty) { + return $list.reverse(accumulator); + } else { + let segment = input.head; + let rest = input.tail; + let _block; + if (segment === "") { + _block = accumulator; + } else if (segment === ".") { + _block = accumulator; + } else if (segment === "..") { + if (accumulator instanceof $Empty) { + _block = accumulator; + } else { + let accumulator$1 = accumulator.tail; + _block = accumulator$1; + } + } else { + let segment$1 = segment; + let accumulator$1 = accumulator; + _block = listPrepend(segment$1, accumulator$1); + } + let accumulator$1 = _block; + loop$input = rest; + loop$accumulator = accumulator$1; + } + } +} + +function remove_dot_segments(input) { + return remove_dot_segments_loop(input, toList([])); +} + +/** + * Splits the path section of a URI into its constituent segments. + * + * Removes empty segments and resolves dot-segments as specified in + * [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC. + * + * ## Examples + * + * ```gleam + * assert path_segments("/users/1") == ["users" ,"1"] + * ``` + */ +export function path_segments(path) { + return remove_dot_segments($string.split(path, "/")); +} + +/** + * Encodes a `Uri` value as a URI string. + * + * The opposite operation is `uri.parse`. + * + * ## Examples + * + * ```gleam + * let uri = Uri(..empty, scheme: Some("https"), host: Some("example.com")) + * assert to_string(uri) == "https://example.com" + * ``` + */ +export function to_string(uri) { + let _block; + let $ = uri.fragment; + if ($ instanceof Some) { + let fragment = $[0]; + _block = toList(["#", fragment]); + } else { + _block = toList([]); + } + let parts = _block; + let _block$1; + let $1 = uri.query; + if ($1 instanceof Some) { + let query = $1[0]; + _block$1 = listPrepend("?", listPrepend(query, parts)); + } else { + _block$1 = parts; + } + let parts$1 = _block$1; + let parts$2 = listPrepend(uri.path, parts$1); + let _block$2; + let $2 = uri.host; + let $3 = $string.starts_with(uri.path, "/"); + if ($2 instanceof Some && !$3) { + let host = $2[0]; + if (host !== "") { + _block$2 = listPrepend("/", parts$2); + } else { + _block$2 = parts$2; + } + } else { + _block$2 = parts$2; + } + let parts$3 = _block$2; + let _block$3; + let $4 = uri.host; + let $5 = uri.port; + if ($4 instanceof Some && $5 instanceof Some) { + let port = $5[0]; + _block$3 = listPrepend(":", listPrepend($int.to_string(port), parts$3)); + } else { + _block$3 = parts$3; + } + let parts$4 = _block$3; + let _block$4; + let $6 = uri.scheme; + let $7 = uri.userinfo; + let $8 = uri.host; + if ($6 instanceof Some) { + if ($7 instanceof Some) { + if ($8 instanceof Some) { + let s = $6[0]; + let u = $7[0]; + let h = $8[0]; + _block$4 = listPrepend( + s, + listPrepend( + "://", + listPrepend(u, listPrepend("@", listPrepend(h, parts$4))), + ), + ); + } else { + let s = $6[0]; + _block$4 = listPrepend(s, listPrepend(":", parts$4)); + } + } else if ($8 instanceof Some) { + let s = $6[0]; + let h = $8[0]; + _block$4 = listPrepend(s, listPrepend("://", listPrepend(h, parts$4))); + } else { + let s = $6[0]; + _block$4 = listPrepend(s, listPrepend(":", parts$4)); + } + } else if ($7 instanceof None && $8 instanceof Some) { + let h = $8[0]; + _block$4 = listPrepend("//", listPrepend(h, parts$4)); + } else { + _block$4 = parts$4; + } + let parts$5 = _block$4; + return $string.concat(parts$5); +} + +/** + * Fetches the origin of a URI. + * + * Returns the origin of a uri as defined in + * [RFC 6454](https://tools.ietf.org/html/rfc6454) + * + * The supported URI schemes are `http` and `https`. + * URLs without a scheme will return `Error`. + * + * ## Examples + * + * ```gleam + * let assert Ok(uri) = parse("https://example.com/path?foo#bar") + * assert origin(uri) == Ok("https://example.com") + * ``` + */ +export function origin(uri) { + let scheme = uri.scheme; + let host = uri.host; + let port = uri.port; + if (host instanceof Some && scheme instanceof Some) { + let $ = scheme[0]; + if ($ === "https" && isEqual(port, new Some(443))) { + let h = host[0]; + return new Ok($string.concat(toList(["https://", h]))); + } else if ($ === "http" && isEqual(port, new Some(80))) { + let h = host[0]; + return new Ok($string.concat(toList(["http://", h]))); + } else { + let s = $; + if ((s === "http") || (s === "https")) { + let h = host[0]; + if (port instanceof Some) { + let p = port[0]; + return new Ok( + $string.concat(toList([s, "://", h, ":", $int.to_string(p)])), + ); + } else { + return new Ok($string.concat(toList([s, "://", h]))); + } + } else { + return new Error(undefined); + } + } + } else { + return new Error(undefined); + } +} + +function join_segments(segments) { + return $string.join(listPrepend("", segments), "/"); +} + +function drop_last(elements) { + return $list.take(elements, $list.length(elements) - 1); +} + +/** + * Resolves a URI with respect to the given base URI. + * + * The base URI must be an absolute URI or this function will return an error. + * The algorithm for merging URIs is described in + * [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2). + */ +export function merge(base, relative) { + let $ = base.scheme; + if ($ instanceof Some) { + let $1 = base.host; + if ($1 instanceof Some) { + let $2 = relative.host; + if ($2 instanceof Some) { + let _block; + let _pipe = relative.path; + let _pipe$1 = $string.split(_pipe, "/"); + let _pipe$2 = remove_dot_segments(_pipe$1); + _block = join_segments(_pipe$2); + let path = _block; + let resolved = new Uri( + $option.or(relative.scheme, base.scheme), + new None(), + relative.host, + $option.or(relative.port, base.port), + path, + relative.query, + relative.fragment, + ); + return new Ok(resolved); + } else { + let _block; + let $4 = relative.path; + if ($4 === "") { + _block = [base.path, $option.or(relative.query, base.query)]; + } else { + let _block$1; + let $5 = $string.starts_with(relative.path, "/"); + if ($5) { + _block$1 = $string.split(relative.path, "/"); + } else { + let _pipe = base.path; + let _pipe$1 = $string.split(_pipe, "/"); + let _pipe$2 = drop_last(_pipe$1); + _block$1 = $list.append(_pipe$2, $string.split(relative.path, "/")); + } + let path_segments$1 = _block$1; + let _block$2; + let _pipe = path_segments$1; + let _pipe$1 = remove_dot_segments(_pipe); + _block$2 = join_segments(_pipe$1); + let path = _block$2; + _block = [path, relative.query]; + } + let $3 = _block; + let new_path = $3[0]; + let new_query = $3[1]; + let resolved = new Uri( + base.scheme, + new None(), + base.host, + base.port, + new_path, + new_query, + relative.fragment, + ); + return new Ok(resolved); + } + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} diff --git a/ui/javascript/gleam_stdlib/gleam@bit_array.erl b/ui/javascript/gleam_stdlib/gleam@bit_array.erl new file mode 100644 index 0000000..8bf7379 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@bit_array.erl @@ -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; + + <> -> + <<<>/binary, + ":size(1)"/utf8>>; + + <> -> + <<<>/binary, + ":size(2)"/utf8>>; + + <> -> + <<<>/binary, + ":size(3)"/utf8>>; + + <> -> + <<<>/binary, + ":size(4)"/utf8>>; + + <> -> + <<<>/binary, + ":size(5)"/utf8>>; + + <> -> + <<<>/binary, + ":size(6)"/utf8>>; + + <> -> + <<<>/binary, + ":size(7)"/utf8>>; + + <> -> + Suffix = case Rest of + <<>> -> + <<""/utf8>>; + + _ -> + <<", "/utf8>> + end, + Accumulator@1 = <<<>/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 + {<>, + <>} -> + 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 + <> when Pref =:= Prefix -> + true; + + _ -> + false + end. diff --git a/ui/javascript/gleam_stdlib/gleam@bool.erl b/ui/javascript/gleam_stdlib/gleam@bool.erl new file mode 100644 index 0000000..edb3be8 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@bool.erl @@ -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. diff --git a/ui/javascript/gleam_stdlib/gleam@bytes_tree.erl b/ui/javascript/gleam_stdlib/gleam@bytes_tree.erl new file mode 100644 index 0000000..dc5cbae --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@bytes_tree.erl @@ -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). diff --git a/ui/javascript/gleam_stdlib/gleam@dict.erl b/ui/javascript/gleam_stdlib/gleam@dict.erl new file mode 100644 index 0000000..cef861c --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@dict.erl @@ -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). diff --git a/ui/javascript/gleam_stdlib/gleam@dynamic.erl b/ui/javascript/gleam_stdlib/gleam@dynamic.erl new file mode 100644 index 0000000..5c260ef --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@dynamic.erl @@ -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). diff --git a/ui/javascript/gleam_stdlib/gleam@dynamic@decode.erl b/ui/javascript/gleam_stdlib/gleam@dynamic@decode.erl new file mode 100644 index 0000000..7ab6d32 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@dynamic@decode.erl @@ -0,0 +1,1121 @@ +-module(gleam@dynamic@decode). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dynamic/decode.gleam"). +-export([decode_dynamic/1, run/2, decode_float/1, map/2, decode_int/1, decode_bit_array/1, decode_string/1, one_of/2, list/1, subfield/3, at/2, success/1, decode_error/2, field/3, optional_field/4, optionally_at/3, decode_bool/1, dict/2, optional/1, map_errors/2, collapse_errors/2, then/2, failure/2, new_primitive_decoder/2, recursive/1]). +-export_type([decode_error/0, decoder/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( + " The `Dynamic` type is used to represent dynamically typed data. That is, data\n" + " that we don't know the precise type of yet, so we need to introspect the data to\n" + " see if it is of the desired type before we can use it. Typically data like this\n" + " would come from user input or from untyped languages such as Erlang or JavaScript.\n" + "\n" + " This module provides the `Decoder` type and associated functions, which provides\n" + " a type-safe and composable way to convert dynamic data into some desired type,\n" + " or into errors if the data doesn't have the desired structure.\n" + "\n" + " The `Decoder` type is generic and has 1 type parameter, which is the type that\n" + " it attempts to decode. A `Decoder(String)` can be used to decode strings, and a\n" + " `Decoder(Option(Int))` can be used to decode `Option(Int)`s\n" + "\n" + " Decoders work using _runtime reflection_ and the data structures of the target\n" + " platform. Differences between Erlang and JavaScript data structures may impact\n" + " your decoders, so it is important to test your decoders on all supported\n" + " platforms.\n" + "\n" + " The decoding technique used by this module was inspired by Juraj Petráš'\n" + " [Toy](https://github.com/Hackder/toy), Go's `encoding/json`, and Elm's\n" + " `Json.Decode`. Thank you to them!\n" + "\n" + " # Generating decoders\n" + "\n" + " The language server has the \"generate dynamic decoder\" code action, which\n" + " will generate a decoder function when run on a custom type definition.\n" + " This generated decoder function can be a convenient shortcut when creating\n" + " your own decoders, and you can edit the generated function to suit your needs.\n" + "\n" + " # Examples\n" + "\n" + " Dynamic data may come from various sources and so many different syntaxes could\n" + " be used to describe or construct them. In these examples a pseudocode\n" + " syntax is used to describe the data.\n" + "\n" + " ## Simple types\n" + "\n" + " This module defines decoders for simple data types such as [`string`](#string),\n" + " [`int`](#int), [`float`](#float), [`bit_array`](#bit_array), and [`bool`](#bool).\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // \"Hello, Joe!\"\n" + "\n" + " let result = decode.run(data, decode.string)\n" + " assert result == Ok(\"Hello, Joe!\")\n" + " ```\n" + "\n" + " ## Lists\n" + "\n" + " The [`list`](#list) decoder decodes `List`s. To use it you must construct it by\n" + " passing in another decoder into the `list` function, which is the decoder that\n" + " is to be used for the elements of the list, type checking both the list and its\n" + " elements.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // [1, 2, 3, 4]\n" + "\n" + " let result = decode.run(data, decode.list(decode.int))\n" + " assert result == Ok([1, 2, 3, 4])\n" + " ```\n" + "\n" + " On Erlang this decoder can decode from lists, and on JavaScript it can\n" + " decode from lists as well as JavaScript arrays.\n" + "\n" + " ## Options\n" + "\n" + " The [`optional`](#optional) decoder is used to decode values that may or may not\n" + " be present. In other environments these might be called \"nullable\" values.\n" + "\n" + " Like the `list` decoder, the `optional` decoder takes another decoder,\n" + " which is used to decode the value if it is present.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // 12.45\n" + "\n" + " let result = decode.run(data, decode.optional(decode.float))\n" + " assert result == Ok(option.Some(12.45))\n" + " ```\n" + " ```gleam\n" + " // Data:\n" + " // null\n" + "\n" + " let result = decode.run(data, decode.optional(decode.int))\n" + " assert result == Ok(option.None)\n" + " ```\n" + "\n" + " This decoder knows how to handle multiple different runtime representations of\n" + " absent values, including `Nil`, `None`, `null`, and `undefined`.\n" + "\n" + " ## Dicts\n" + "\n" + " The [`dict`](#dict) decoder decodes `Dicts` and contains two other decoders, one\n" + " for the keys, one for the values.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // { \"Lucy\" -> 10, \"Nubi\" -> 20 }\n" + "\n" + " let result = decode.run(data, decode.dict(decode.string, decode.int))\n" + " assert result == Ok(dict.from_list([\n" + " #(\"Lucy\", 10),\n" + " #(\"Nubi\", 20),\n" + " ]))\n" + " ```\n" + "\n" + " ## Indexing objects\n" + "\n" + " The [`at`](#at) decoder can be used to decode a value that is nested within\n" + " key-value containers such as Gleam dicts, Erlang maps, or JavaScript objects.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // { \"one\" -> { \"two\" -> 123 } }\n" + "\n" + " let result = decode.run(data, decode.at([\"one\", \"two\"], decode.int))\n" + " assert result == Ok(123)\n" + " ```\n" + "\n" + " ## Indexing arrays\n" + "\n" + " If you use ints as keys then the [`at`](#at) decoder can be used to index into\n" + " array-like containers such as Gleam or Erlang tuples, or JavaScript arrays.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // [\"one\", \"two\", \"three\"]\n" + "\n" + " let result = decode.run(data, decode.at([1], decode.string))\n" + " assert result == Ok(\"two\")\n" + " ```\n" + "\n" + " ## Records\n" + "\n" + " Decoding records from dynamic data is more complex and requires combining a\n" + " decoder for each field and a special constructor that builds your records with\n" + " the decoded field values.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // {\n" + " // \"score\" -> 180,\n" + " // \"name\" -> \"Mel Smith\",\n" + " // \"is-admin\" -> false,\n" + " // \"enrolled\" -> true,\n" + " // \"colour\" -> \"Red\",\n" + " // }\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use score <- decode.field(\"score\", decode.int)\n" + " use colour <- decode.field(\"colour\", decode.string)\n" + " use enrolled <- decode.field(\"enrolled\", decode.bool)\n" + " decode.success(Player(name:, score:, colour:, enrolled:))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(Player(\"Mel Smith\", 180, \"Red\", True))\n" + " ```\n" + "\n" + " ## Enum variants\n" + "\n" + " Imagine you have a custom type where all the variants do not contain any values.\n" + "\n" + " ```gleam\n" + " pub type PocketMonsterType {\n" + " Fire\n" + " Water\n" + " Grass\n" + " Electric\n" + " }\n" + " ```\n" + "\n" + " You might choose to encode these variants as strings, `\"fire\"` for `Fire`,\n" + " `\"water\"` for `Water`, and so on. To decode them you'll need to decode the dynamic\n" + " data as a string, but then you'll need to decode it further still as not all\n" + " strings are valid values for the enum. This can be done with the `then`\n" + " function, which enables running a second decoder after the first one\n" + " succeeds.\n" + "\n" + " ```gleam\n" + " let decoder = {\n" + " use decoded_string <- decode.then(decode.string)\n" + " case decoded_string {\n" + " // Return succeeding decoders for valid strings\n" + " \"fire\" -> decode.success(Fire)\n" + " \"water\" -> decode.success(Water)\n" + " \"grass\" -> decode.success(Grass)\n" + " \"electric\" -> decode.success(Electric)\n" + " // Return a failing decoder for any other strings\n" + " _ -> decode.failure(Fire, expected: \"PocketMonsterType\")\n" + " }\n" + " }\n" + "\n" + " let result = decode.run(dynamic.string(\"water\"), decoder)\n" + " assert result == Ok(Water)\n" + "\n" + " let result = decode.run(dynamic.string(\"wobble\"), decoder)\n" + " assert result == Error([DecodeError(\"PocketMonsterType\", \"String\", [])])\n" + " ```\n" + "\n" + " ## Record variants\n" + "\n" + " Decoding type variants that contain other values is done by combining the\n" + " techniques from the \"enum variants\" and \"records\" examples. Imagine you have\n" + " this custom type that you want to decode:\n" + "\n" + " ```gleam\n" + " pub type PocketMonsterPerson {\n" + " Trainer(name: String, badge_count: Int)\n" + " GymLeader(name: String, speciality: PocketMonsterType)\n" + " }\n" + " ```\n" + " And you would like to be able to decode these from dynamic data like this:\n" + " ```erlang\n" + " {\n" + " \"type\" -> \"trainer\",\n" + " \"name\" -> \"Ash\",\n" + " \"badge-count\" -> 1,\n" + " }\n" + " ```\n" + " ```erlang\n" + " {\n" + " \"type\" -> \"gym-leader\",\n" + " \"name\" -> \"Misty\",\n" + " \"speciality\" -> \"water\",\n" + " }\n" + " ```\n" + "\n" + " Notice how both documents have a `\"type\"` field, which is used to indicate which\n" + " variant the data is for.\n" + "\n" + " First, define decoders for each of the variants:\n" + "\n" + " ```gleam\n" + " let trainer_decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use badge_count <- decode.field(\"badge-count\", decode.int)\n" + " decode.success(Trainer(name, badge_count))\n" + " }\n" + "\n" + " let gym_leader_decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use speciality <- decode.field(\"speciality\", pocket_monster_type_decoder)\n" + " decode.success(GymLeader(name, speciality))\n" + " }\n" + " ```\n" + "\n" + " A third decoder can be used to extract and decode the `\"type\"` field, and the\n" + " expression can evaluate to whichever decoder is suitable for the document.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // {\n" + " // \"type\" -> \"gym-leader\",\n" + " // \"name\" -> \"Misty\",\n" + " // \"speciality\" -> \"water\",\n" + " // }\n" + "\n" + " let decoder = {\n" + " use tag <- decode.field(\"type\", decode.string)\n" + " case tag {\n" + " \"gym-leader\" -> gym_leader_decoder\n" + " _ -> trainer_decoder\n" + " }\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(GymLeader(\"Misty\", Water))\n" + " ```\n" +). + +-type decode_error() :: {decode_error, binary(), binary(), list(binary())}. + +-opaque decoder(BVN) :: {decoder, + fun((gleam@dynamic:dynamic_()) -> {BVN, list(decode_error())})}. + +-file("src/gleam/dynamic/decode.gleam", 742). +-spec decode_dynamic(gleam@dynamic:dynamic_()) -> {gleam@dynamic:dynamic_(), + list(decode_error())}. +decode_dynamic(Data) -> + {Data, []}. + +-file("src/gleam/dynamic/decode.gleam", 364). +?DOC( + " Run a decoder on a `Dynamic` value, decoding the value if it is of the\n" + " desired type, or returning errors.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use email <- decode.field(\"email\", decode.string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " decode.run(data, decoder)\n" + " ```\n" +). +-spec run(gleam@dynamic:dynamic_(), decoder(BVV)) -> {ok, BVV} | + {error, list(decode_error())}. +run(Data, Decoder) -> + {Maybe_invalid_data, Errors} = (erlang:element(2, Decoder))(Data), + case Errors of + [] -> + {ok, Maybe_invalid_data}; + + [_ | _] -> + {error, Errors} + end. + +-file("src/gleam/dynamic/decode.gleam", 617). +-spec run_dynamic_function( + gleam@dynamic:dynamic_(), + binary(), + fun((gleam@dynamic:dynamic_()) -> {ok, BXQ} | {error, BXQ}) +) -> {BXQ, list(decode_error())}. +run_dynamic_function(Data, Name, F) -> + case F(Data) of + {ok, Data@1} -> + {Data@1, []}; + + {error, Placeholder} -> + {Placeholder, + [{decode_error, Name, gleam_stdlib:classify_dynamic(Data), []}]} + end. + +-file("src/gleam/dynamic/decode.gleam", 723). +-spec decode_float(gleam@dynamic:dynamic_()) -> {float(), list(decode_error())}. +decode_float(Data) -> + run_dynamic_function(Data, <<"Float"/utf8>>, fun gleam_stdlib:float/1). + +-file("src/gleam/dynamic/decode.gleam", 902). +?DOC( + " Apply a transformation function to any value decoded by the decoder.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.int |> decode.map(int.to_string)\n" + " let result = decode.run(dynamic.int(1000), decoder)\n" + " assert result == Ok(\"1000\")\n" + " ```\n" +). +-spec map(decoder(BZT), fun((BZT) -> BZV)) -> decoder(BZV). +map(Decoder, Transformer) -> + {decoder, + fun(D) -> + {Data, Errors} = (erlang:element(2, Decoder))(D), + {Transformer(Data), Errors} + end}. + +-file("src/gleam/dynamic/decode.gleam", 697). +-spec decode_int(gleam@dynamic:dynamic_()) -> {integer(), list(decode_error())}. +decode_int(Data) -> + run_dynamic_function(Data, <<"Int"/utf8>>, fun gleam_stdlib:int/1). + +-file("src/gleam/dynamic/decode.gleam", 757). +-spec decode_bit_array(gleam@dynamic:dynamic_()) -> {bitstring(), + list(decode_error())}. +decode_bit_array(Data) -> + run_dynamic_function( + Data, + <<"BitArray"/utf8>>, + fun gleam_stdlib:bit_array/1 + ). + +-file("src/gleam/dynamic/decode.gleam", 646). +-spec dynamic_string(gleam@dynamic:dynamic_()) -> {ok, binary()} | + {error, binary()}. +dynamic_string(Data) -> + case gleam_stdlib:bit_array(Data) of + {ok, Data@1} -> + case gleam@bit_array:to_string(Data@1) of + {ok, String} -> + {ok, String}; + + {error, _} -> + {error, <<""/utf8>>} + end; + + {error, _} -> + {error, <<""/utf8>>} + end. + +-file("src/gleam/dynamic/decode.gleam", 641). +-spec decode_string(gleam@dynamic:dynamic_()) -> {binary(), + list(decode_error())}. +decode_string(Data) -> + run_dynamic_function(Data, <<"String"/utf8>>, fun dynamic_string/1). + +-file("src/gleam/dynamic/decode.gleam", 991). +-spec run_decoders( + gleam@dynamic:dynamic_(), + {CAP, list(decode_error())}, + list(decoder(CAP)) +) -> {CAP, list(decode_error())}. +run_decoders(Data, Failure, Decoders) -> + case Decoders of + [] -> + Failure; + + [Decoder | Decoders@1] -> + {_, Errors} = Layer = (erlang:element(2, Decoder))(Data), + case Errors of + [] -> + Layer; + + [_ | _] -> + run_decoders(Data, Failure, Decoders@1) + end + end. + +-file("src/gleam/dynamic/decode.gleam", 978). +?DOC( + " Create a new decoder from several other decoders. Each of the inner\n" + " decoders is run in turn, and the value from the first to succeed is used.\n" + "\n" + " If no decoder succeeds then the errors from the first decoder are used.\n" + " If you wish for different errors then you may wish to use the\n" + " `collapse_errors` or `map_errors` functions.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.one_of(decode.string, or: [\n" + " decode.int |> decode.map(int.to_string),\n" + " decode.float |> decode.map(float.to_string),\n" + " ])\n" + " assert decode.run(dynamic.int(1000), decoder) == Ok(\"1000\")\n" + " ```\n" +). +-spec one_of(decoder(CAK), list(decoder(CAK))) -> decoder(CAK). +one_of(First, Alternatives) -> + {decoder, + fun(Dynamic_data) -> + {_, Errors} = Layer = (erlang:element(2, First))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + run_decoders(Dynamic_data, Layer, Alternatives) + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 457). +-spec path_segment_to_string(gleam@dynamic:dynamic_()) -> binary(). +path_segment_to_string(Key) -> + Decoder = one_of( + {decoder, fun decode_string/1}, + [begin + _pipe = {decoder, fun decode_int/1}, + map(_pipe, fun erlang:integer_to_binary/1) + end, + begin + _pipe@1 = {decoder, fun decode_float/1}, + map(_pipe@1, fun gleam_stdlib:float_to_string/1) + end] + ), + case run(Key, Decoder) of + {ok, Key@1} -> + Key@1; + + {error, _} -> + <<<<"<"/utf8, (gleam_stdlib:classify_dynamic(Key))/binary>>/binary, + ">"/utf8>> + end. + +-file("src/gleam/dynamic/decode.gleam", 445). +-spec push_path({BWR, list(decode_error())}, list(any())) -> {BWR, + list(decode_error())}. +push_path(Layer, Path) -> + Path@1 = gleam@list:map(Path, fun(Key) -> _pipe = Key, + _pipe@1 = gleam_stdlib:identity(_pipe), + path_segment_to_string(_pipe@1) end), + Errors = gleam@list:map( + erlang:element(2, Layer), + fun(Error) -> + {decode_error, + erlang:element(2, Error), + erlang:element(3, Error), + lists:append(Path@1, erlang:element(4, Error))} + end + ), + {erlang:element(1, Layer), Errors}. + +-file("src/gleam/dynamic/decode.gleam", 779). +?DOC( + " A decoder that decodes lists where all elements are decoded with a given\n" + " decoder.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let result =\n" + " [1, 2, 3]\n" + " |> list.map(dynamic.int)\n" + " |> dynamic.list\n" + " |> decode.run(decode.list(of: decode.int))\n" + " assert result == Ok([1, 2, 3])\n" + " ```\n" +). +-spec list(decoder(BYI)) -> decoder(list(BYI)). +list(Inner) -> + {decoder, + fun(Data) -> + gleam_stdlib:list( + Data, + erlang:element(2, Inner), + fun(P, K) -> push_path(P, [K]) end, + 0, + [] + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 409). +-spec index( + list(BWF), + list(BWF), + fun((gleam@dynamic:dynamic_()) -> {BWI, list(decode_error())}), + gleam@dynamic:dynamic_(), + fun((gleam@dynamic:dynamic_(), list(BWF)) -> {BWI, list(decode_error())}) +) -> {BWI, list(decode_error())}. +index(Path, Position, Inner, Data, Handle_miss) -> + case Path of + [] -> + _pipe = Data, + _pipe@1 = Inner(_pipe), + push_path(_pipe@1, lists:reverse(Position)); + + [Key | Path@1] -> + case gleam_stdlib:index(Data, Key) of + {ok, {some, Data@1}} -> + index(Path@1, [Key | Position], Inner, Data@1, Handle_miss); + + {ok, none} -> + Handle_miss(Data, [Key | Position]); + + {error, Kind} -> + {Default, _} = Inner(Data), + _pipe@2 = {Default, + [{decode_error, + Kind, + gleam_stdlib:classify_dynamic(Data), + []}]}, + push_path(_pipe@2, lists:reverse(Position)) + end + end. + +-file("src/gleam/dynamic/decode.gleam", 332). +?DOC( + " The same as [`field`](#field), except taking a path to the value rather\n" + " than a field name.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"data\"), dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ])\n" + " ])\n" + "\n" + " let decoder = {\n" + " use name <- decode.subfield([\"data\", \"name\"], decode.string)\n" + " use email <- decode.subfield([\"data\", \"email\"], decode.string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" +). +-spec subfield(list(any()), decoder(BVQ), fun((BVQ) -> decoder(BVS))) -> decoder(BVS). +subfield(Field_path, Field_decoder, Next) -> + {decoder, + fun(Data) -> + {Out, Errors1} = index( + Field_path, + [], + erlang:element(2, Field_decoder), + Data, + fun(Data@1, Position) -> + {Default, _} = (erlang:element(2, Field_decoder))(Data@1), + _pipe = {Default, + [{decode_error, + <<"Field"/utf8>>, + <<"Nothing"/utf8>>, + []}]}, + push_path(_pipe, lists:reverse(Position)) + end + ), + {Out@1, Errors2} = (erlang:element(2, Next(Out)))(Data), + {Out@1, lists:append(Errors1, Errors2)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 399). +?DOC( + " A decoder that decodes a value that is nested within other values. For\n" + " example, decoding a value that is within some deeply nested JSON objects.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.at([\"one\", \"two\"], decode.int)\n" + "\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.properties([\n" + " #(dynamic.string(\"two\"), dynamic.int(1000)),\n" + " ]),\n" + " ])\n" + "\n" + " assert decode.run(data, decoder) == Ok(1000)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert dynamic.nil()\n" + " |> decode.run(decode.optional(decode.int))\n" + " == Ok(option.None)\n" + " ```\n" +). +-spec at(list(any()), decoder(BWC)) -> decoder(BWC). +at(Path, Inner) -> + {decoder, + fun(Data) -> + index( + Path, + [], + erlang:element(2, Inner), + Data, + fun(Data@1, Position) -> + {Default, _} = (erlang:element(2, Inner))(Data@1), + _pipe = {Default, + [{decode_error, + <<"Field"/utf8>>, + <<"Nothing"/utf8>>, + []}]}, + push_path(_pipe, lists:reverse(Position)) + end + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 489). +?DOC( + " Finalise a decoder having successfully extracted a value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ])\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.field(\"email\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" +). +-spec success(BWW) -> decoder(BWW). +success(Data) -> + {decoder, fun(_) -> {Data, []} end}. + +-file("src/gleam/dynamic/decode.gleam", 495). +?DOC(" Construct a decode error for some unexpected dynamic data.\n"). +-spec decode_error(binary(), gleam@dynamic:dynamic_()) -> list(decode_error()). +decode_error(Expected, Found) -> + [{decode_error, Expected, gleam_stdlib:classify_dynamic(Found), []}]. + +-file("src/gleam/dynamic/decode.gleam", 534). +?DOC( + " Run a decoder on a field of a `Dynamic` value, decoding the value if it is\n" + " of the desired type, or returning errors. An error is returned if there is\n" + " no field for the specified key.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ])\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.field(\"email\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" + "\n" + " If you wish to decode a value that is more deeply nested within the dynamic\n" + " data, see [`subfield`](#subfield) and [`at`](#at).\n" + "\n" + " If you wish to return a default in the event that a field is not present,\n" + " see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at).\n" +). +-spec field(any(), decoder(BXA), fun((BXA) -> decoder(BXC))) -> decoder(BXC). +field(Field_name, Field_decoder, Next) -> + subfield([Field_name], Field_decoder, Next). + +-file("src/gleam/dynamic/decode.gleam", 567). +?DOC( + " Run a decoder on a field of a `Dynamic` value, decoding the value if it is\n" + " of the desired type, or returning errors. The given default value is\n" + " returned if there is no field for the specified key.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ])\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.optional_field(\"email\", \"n/a\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"n/a\"))\n" + " ```\n" +). +-spec optional_field(any(), BXG, decoder(BXG), fun((BXG) -> decoder(BXI))) -> decoder(BXI). +optional_field(Key, Default, Field_decoder, Next) -> + {decoder, + fun(Data) -> + {Out, Errors1} = begin + _pipe = case gleam_stdlib:index(Data, Key) of + {ok, {some, Data@1}} -> + (erlang:element(2, Field_decoder))(Data@1); + + {ok, none} -> + {Default, []}; + + {error, Kind} -> + {Default, + [{decode_error, + Kind, + gleam_stdlib:classify_dynamic(Data), + []}]} + end, + push_path(_pipe, [Key]) + end, + {Out@1, Errors2} = (erlang:element(2, Next(Out)))(Data), + {Out@1, lists:append(Errors1, Errors2)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 607). +?DOC( + " A decoder that decodes a value that is nested within other values. For\n" + " example, decoding a value that is within some deeply nested JSON objects.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.optionally_at([\"one\", \"two\"], 100, decode.int)\n" + "\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.properties([])),\n" + " ])\n" + "\n" + " assert decode.run(data, decoder) == Ok(100)\n" + " ```\n" +). +-spec optionally_at(list(any()), BXN, decoder(BXN)) -> decoder(BXN). +optionally_at(Path, Default, Inner) -> + {decoder, + fun(Data) -> + index( + Path, + [], + erlang:element(2, Inner), + Data, + fun(_, _) -> {Default, []} end + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 668). +-spec decode_bool(gleam@dynamic:dynamic_()) -> {boolean(), list(decode_error())}. +decode_bool(Data) -> + case gleam_stdlib:identity(true) =:= Data of + true -> + {true, []}; + + false -> + case gleam_stdlib:identity(false) =:= Data of + true -> + {false, []}; + + false -> + {false, decode_error(<<"Bool"/utf8>>, Data)} + end + end. + +-file("src/gleam/dynamic/decode.gleam", 831). +-spec fold_dict( + {gleam@dict:dict(BZB, BZC), list(decode_error())}, + gleam@dynamic:dynamic_(), + gleam@dynamic:dynamic_(), + fun((gleam@dynamic:dynamic_()) -> {BZB, list(decode_error())}), + fun((gleam@dynamic:dynamic_()) -> {BZC, list(decode_error())}) +) -> {gleam@dict:dict(BZB, BZC), list(decode_error())}. +fold_dict(Acc, Key, Value, Key_decoder, Value_decoder) -> + case Key_decoder(Key) of + {Key_decoded, []} -> + case Value_decoder(Value) of + {Value@1, []} -> + Dict = gleam@dict:insert( + erlang:element(1, Acc), + Key_decoded, + Value@1 + ), + {Dict, erlang:element(2, Acc)}; + + {_, Errors} -> + Key_identifier = path_segment_to_string(Key), + push_path({maps:new(), Errors}, [Key_identifier]) + end; + + {_, Errors@1} -> + push_path({maps:new(), Errors@1}, [<<"keys"/utf8>>]) + end. + +-file("src/gleam/dynamic/decode.gleam", 811). +?DOC( + " A decoder that decodes dicts where all keys and values are decoded with\n" + " given decoders.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let values = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.int(1)),\n" + " #(dynamic.string(\"two\"), dynamic.int(2)),\n" + " ])\n" + "\n" + " let result =\n" + " decode.run(values, decode.dict(decode.string, decode.int))\n" + " assert result == Ok(values)\n" + " ```\n" +). +-spec dict(decoder(BYU), decoder(BYW)) -> decoder(gleam@dict:dict(BYU, BYW)). +dict(Key, Value) -> + {decoder, fun(Data) -> case gleam_stdlib:dict(Data) of + {error, _} -> + {maps:new(), decode_error(<<"Dict"/utf8>>, Data)}; + + {ok, Dict} -> + gleam@dict:fold( + Dict, + {maps:new(), []}, + fun(A, K, V) -> case erlang:element(2, A) of + [] -> + fold_dict( + A, + K, + V, + erlang:element(2, Key), + erlang:element(2, Value) + ); + + [_ | _] -> + A + end end + ) + end end}. + +-file("src/gleam/dynamic/decode.gleam", 880). +?DOC( + " A decoder that decodes nullable values of a type decoded by with a given\n" + " decoder.\n" + "\n" + " This function can handle common representations of null on all runtimes, such as\n" + " `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on\n" + " JavaScript.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let result = decode.run(dynamic.int(100), decode.optional(decode.int))\n" + " assert result == Ok(option.Some(100))\n" + " ```\n" + "\n" + " ```gleam\n" + " let result = decode.run(dynamic.nil(), decode.optional(decode.int))\n" + " assert result == Ok(option.None)\n" + " ```\n" +). +-spec optional(decoder(BZP)) -> decoder(gleam@option:option(BZP)). +optional(Inner) -> + {decoder, fun(Data) -> case gleam_stdlib:is_null(Data) of + true -> + {none, []}; + + false -> + {Data@1, Errors} = (erlang:element(2, Inner))(Data), + {{some, Data@1}, Errors} + end end}. + +-file("src/gleam/dynamic/decode.gleam", 911). +?DOC(" Apply a transformation function to any errors returned by the decoder.\n"). +-spec map_errors( + decoder(BZX), + fun((list(decode_error())) -> list(decode_error())) +) -> decoder(BZX). +map_errors(Decoder, Transformer) -> + {decoder, + fun(D) -> + {Data, Errors} = (erlang:element(2, Decoder))(D), + {Data, Transformer(Errors)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 935). +?DOC( + " Replace all errors produced by a decoder with one single error for a named\n" + " expected type.\n" + "\n" + " This function may be useful if you wish to simplify errors before\n" + " presenting them to a user, particularly when using the `one_of` function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.string |> decode.collapse_errors(\"MyThing\")\n" + " let result = decode.run(dynamic.int(1000), decoder)\n" + " assert result == Error([DecodeError(\"MyThing\", \"Int\", [])])\n" + " ```\n" +). +-spec collapse_errors(decoder(CAC), binary()) -> decoder(CAC). +collapse_errors(Decoder, Name) -> + {decoder, + fun(Dynamic_data) -> + {Data, Errors} = Layer = (erlang:element(2, Decoder))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + {Data, decode_error(Name, Dynamic_data)} + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 949). +?DOC( + " Create a new decoder based upon the value of a previous decoder.\n" + "\n" + " This may be useful to run one previous decoder to use in further decoding.\n" +). +-spec then(decoder(CAF), fun((CAF) -> decoder(CAH))) -> decoder(CAH). +then(Decoder, Next) -> + {decoder, + fun(Dynamic_data) -> + {Data, Errors} = (erlang:element(2, Decoder))(Dynamic_data), + Decoder@1 = Next(Data), + {Data@1, _} = Layer = (erlang:element(2, Decoder@1))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + {Data@1, Errors} + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 1024). +?DOC( + " Define a decoder that always fails.\n" + "\n" + " The first parameter is a \"placeholder\" value, which is some default value that the\n" + " decoder uses internally in place of the value that would have been produced\n" + " if the decoder was successful. It doesn't matter what this value is, it is\n" + " never returned by the decoder or shown to the user, so pick some arbitrary\n" + " value. If it is an int you might pick `0`, if it is a list you might pick\n" + " `[]`.\n" + "\n" + " The second parameter is the name of the type that has failed to decode.\n" + "\n" + " ```gleam\n" + " decode.failure(User(name: \"\", score: 0, tags: []), expected: \"User\")\n" + " ```\n" +). +-spec failure(CAU, binary()) -> decoder(CAU). +failure(Placeholder, Name) -> + {decoder, fun(D) -> {Placeholder, decode_error(Name, D)} end}. + +-file("src/gleam/dynamic/decode.gleam", 1065). +?DOC( + " Create a decoder for a new data type from a decoding function.\n" + "\n" + " This function is used for new primitive types. For example, you might\n" + " define a decoder for Erlang's pid type.\n" + "\n" + " A default \"placeholder\" value is also required to make a decoder. When this\n" + " decoder is used as part of a larger decoder this placeholder value is used\n" + " so that the rest of the decoder can continue to run and\n" + " collect all decoding errors. It doesn't matter what this value is, it is\n" + " never returned by the decoder or shown to the user, so pick some arbitrary\n" + " value. If it is an int you might pick `0`, if it is a list you might pick\n" + " `[]`.\n" + "\n" + " If you were to make a decoder for the `Int` type (rather than using the\n" + " built-in `Int` decoder) you would define it like so:\n" + "\n" + " ```gleam\n" + " pub fn int_decoder() -> decode.Decoder(Int) {\n" + " let default = \"\"\n" + " decode.new_primitive_decoder(\"Int\", int_from_dynamic)\n" + " }\n" + "\n" + " @external(erlang, \"my_module\", \"int_from_dynamic\")\n" + " fn int_from_dynamic(data: Int) -> Result(Int, Int)\n" + " ```\n" + "\n" + " ```erlang\n" + " -module(my_module).\n" + " -export([int_from_dynamic/1]).\n" + "\n" + " int_from_dynamic(Data) ->\n" + " case is_integer(Data) of\n" + " true -> {ok, Data};\n" + " false -> {error, 0}\n" + " end.\n" + " ```\n" +). +-spec new_primitive_decoder( + binary(), + fun((gleam@dynamic:dynamic_()) -> {ok, CAW} | {error, CAW}) +) -> decoder(CAW). +new_primitive_decoder(Name, Decoding_function) -> + {decoder, fun(D) -> case Decoding_function(D) of + {ok, T} -> + {T, []}; + + {error, Placeholder} -> + {Placeholder, + [{decode_error, + Name, + gleam_stdlib:classify_dynamic(D), + []}]} + end end}. + +-file("src/gleam/dynamic/decode.gleam", 1102). +?DOC( + " Create a decoder that can refer to itself, useful for decoding deeply\n" + " nested data.\n" + "\n" + " Attempting to create a recursive decoder without this function could result\n" + " in an infinite loop. If you are using `field` or other `use`able functions\n" + " then you may not need to use this function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " type Nested {\n" + " Nested(List(Nested))\n" + " Value(String)\n" + " }\n" + "\n" + " fn nested_decoder() -> decode.Decoder(Nested) {\n" + " use <- decode.recursive\n" + " decode.one_of(decode.string |> decode.map(Value), [\n" + " decode.list(nested_decoder()) |> decode.map(Nested),\n" + " ])\n" + " }\n" + " ```\n" +). +-spec recursive(fun(() -> decoder(CBA))) -> decoder(CBA). +recursive(Inner) -> + {decoder, + fun(Data) -> + Decoder = Inner(), + (erlang:element(2, Decoder))(Data) + end}. diff --git a/ui/javascript/gleam_stdlib/gleam@float.erl b/ui/javascript/gleam_stdlib/gleam@float.erl new file mode 100644 index 0000000..e420d17 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@float.erl @@ -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: \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). diff --git a/ui/javascript/gleam_stdlib/gleam@function.erl b/ui/javascript/gleam_stdlib/gleam@function.erl new file mode 100644 index 0000000..63fff58 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@function.erl @@ -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. diff --git a/ui/javascript/gleam_stdlib/gleam@int.erl b/ui/javascript/gleam_stdlib/gleam@int.erl new file mode 100644 index 0000000..3504942 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@int.erl @@ -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). diff --git a/ui/javascript/gleam_stdlib/gleam@io.erl b/ui/javascript/gleam_stdlib/gleam@io.erl new file mode 100644 index 0000000..4b68e26 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@io.erl @@ -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). diff --git a/ui/javascript/gleam_stdlib/gleam@list.erl b/ui/javascript/gleam_stdlib/gleam@list.erl new file mode 100644 index 0000000..510e22c --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@list.erl @@ -0,0 +1,2716 @@ +-module(gleam@list). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/list.gleam"). +-export([length/1, count/2, reverse/1, is_empty/1, contains/2, first/1, rest/1, group/2, filter/2, filter_map/2, map/2, map2/3, map_fold/3, index_map/2, try_map/2, drop/2, take/2, new/0, wrap/1, append/2, prepend/2, flatten/1, flat_map/2, fold/3, fold_right/3, index_fold/3, try_fold/3, fold_until/3, find/2, find_map/2, all/2, any/2, zip/2, strict_zip/2, unzip/1, intersperse/2, unique/1, sort/2, repeat/2, split/2, split_while/2, key_find/2, key_filter/2, key_pop/2, key_set/3, each/2, try_each/2, partition/2, permutations/1, window/2, window_by_2/1, drop_while/2, take_while/2, chunk/2, sized_chunk/2, reduce/2, scan/3, last/1, combinations/2, combination_pairs/1, transpose/1, interleave/1, shuffle/1, max/2, sample/2]). +-export_type([continue_or_stop/1, sorting/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( + " Lists are an ordered sequence of elements and are one of the most common\n" + " data types in Gleam.\n" + "\n" + " New elements can be added and removed from the front of a list in\n" + " constant time, while adding and removing from the end requires traversing\n" + " and copying the whole list, so keep this in mind when designing your\n" + " programs.\n" + "\n" + " There is a dedicated syntax for prefixing to a list:\n" + "\n" + " ```gleam\n" + " let new_list = [1, 2, ..existing_list]\n" + " ```\n" + "\n" + " And a matching syntax for getting the first elements of a list:\n" + "\n" + " ```gleam\n" + " case list {\n" + " [first_element, ..rest] -> first_element\n" + " _ -> \"this pattern matches when the list is empty\"\n" + " }\n" + " ```\n" + "\n" +). + +-type continue_or_stop(AAE) :: {continue, AAE} | {stop, AAE}. + +-type sorting() :: ascending | descending. + +-file("src/gleam/list.gleam", 57). +-spec length_loop(list(any()), integer()) -> integer(). +length_loop(List, Count) -> + case List of + [_ | List@1] -> + length_loop(List@1, Count + 1); + + [] -> + Count + end. + +-file("src/gleam/list.gleam", 53). +?DOC( + " Counts the number of elements in a given list.\n" + "\n" + " This function has to traverse the list to determine the number of elements,\n" + " so it runs in linear time.\n" + "\n" + " This function is natively implemented by the virtual machine and is highly\n" + " optimised.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert length([]) == 0\n" + " ```\n" + "\n" + " ```gleam\n" + " assert length([1]) == 1\n" + " ```\n" + "\n" + " ```gleam\n" + " assert length([1, 2]) == 2\n" + " ```\n" +). +-spec length(list(any())) -> integer(). +length(List) -> + erlang:length(List). + +-file("src/gleam/list.gleam", 87). +-spec count_loop(list(AAL), fun((AAL) -> boolean()), integer()) -> integer(). +count_loop(List, Predicate, Acc) -> + case List of + [] -> + Acc; + + [First | Rest] -> + case Predicate(First) of + true -> + count_loop(Rest, Predicate, Acc + 1); + + false -> + count_loop(Rest, Predicate, Acc) + end + end. + +-file("src/gleam/list.gleam", 83). +?DOC( + " Counts the number of elements in a given list satisfying a given predicate.\n" + "\n" + " This function has to traverse the list to determine the number of elements,\n" + " so it runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert count([], fn(a) { a > 0 }) == 0\n" + " ```\n" + "\n" + " ```gleam\n" + " assert count([1], fn(a) { a > 0 }) == 1\n" + " ```\n" + "\n" + " ```gleam\n" + " assert count([1, 2, 3], int.is_odd) == 2\n" + " ```\n" +). +-spec count(list(AAJ), fun((AAJ) -> boolean())) -> integer(). +count(List, Predicate) -> + count_loop(List, Predicate, 0). + +-file("src/gleam/list.gleam", 122). +?DOC( + " Creates a new list from a given list containing the same elements but in the\n" + " opposite order.\n" + "\n" + " This function has to traverse the list to create the new reversed list, so\n" + " it runs in linear time.\n" + "\n" + " This function is natively implemented by the virtual machine and is highly\n" + " optimised.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert reverse([]) == []\n" + " ```\n" + "\n" + " ```gleam\n" + " assert reverse([1]) == [1]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert reverse([1, 2]) == [2, 1]\n" + " ```\n" +). +-spec reverse(list(AAN)) -> list(AAN). +reverse(List) -> + lists:reverse(List). + +-file("src/gleam/list.gleam", 156). +?DOC( + " Determines whether or not the list is empty.\n" + "\n" + " This function runs in constant time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert is_empty([])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !is_empty([1])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !is_empty([1, 1])\n" + " ```\n" +). +-spec is_empty(list(any())) -> boolean(). +is_empty(List) -> + List =:= []. + +-file("src/gleam/list.gleam", 187). +?DOC( + " Determines whether or not a given element exists within a given list.\n" + "\n" + " This function traverses the list to find the element, so it runs in linear\n" + " time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert !contains([], any: 0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert [0] |> contains(any: 0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !contains([1], any: 0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !contains([1, 1], any: 0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert [1, 0] |> contains(any: 0)\n" + " ```\n" +). +-spec contains(list(AAW), AAW) -> boolean(). +contains(List, Elem) -> + case List of + [] -> + false; + + [First | _] when First =:= Elem -> + true; + + [_ | Rest] -> + contains(Rest, Elem) + end. + +-file("src/gleam/list.gleam", 211). +?DOC( + " Gets the first element from the start of the list, if there is one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert first([]) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert first([0]) == Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert first([1, 2]) == Ok(1)\n" + " ```\n" +). +-spec first(list(AAY)) -> {ok, AAY} | {error, nil}. +first(List) -> + case List of + [] -> + {error, nil}; + + [First | _] -> + {ok, First} + end. + +-file("src/gleam/list.gleam", 237). +?DOC( + " Returns the list minus the first element. If the list is empty, `Error(Nil)` is\n" + " returned.\n" + "\n" + " This function runs in constant time and does not make a copy of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert rest([]) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert rest([0]) == Ok([])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert rest([1, 2]) == Ok([2])\n" + " ```\n" +). +-spec rest(list(ABC)) -> {ok, list(ABC)} | {error, nil}. +rest(List) -> + case List of + [] -> + {error, nil}; + + [_ | Rest] -> + {ok, Rest} + end. + +-file("src/gleam/list.gleam", 276). +?DOC( + " Groups the elements from the given list by the given key function.\n" + "\n" + " Does not preserve the initial value order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/dict\n" + "\n" + " assert\n" + " [Ok(3), Error(\"Wrong\"), Ok(200), Ok(73)]\n" + " |> group(by: fn(i) {\n" + " case i {\n" + " Ok(_) -> \"Successful\"\n" + " Error(_) -> \"Failed\"\n" + " }\n" + " })\n" + " |> dict.to_list\n" + " == [\n" + " #(\"Failed\", [Error(\"Wrong\")]),\n" + " #(\"Successful\", [Ok(73), Ok(200), Ok(3)])\n" + " ]\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/dict\n" + "\n" + " assert group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 })\n" + " |> dict.to_list\n" + " == [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])]\n" + " ```\n" +). +-spec group(list(ABH), fun((ABH) -> ABJ)) -> gleam@dict:dict(ABJ, list(ABH)). +group(List, Key) -> + gleam@dict:group(Key, List). + +-file("src/gleam/list.gleam", 297). +-spec filter_loop(list(ABQ), fun((ABQ) -> boolean()), list(ABQ)) -> list(ABQ). +filter_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + New_acc = case Fun(First) of + true -> + [First | Acc]; + + false -> + Acc + end, + filter_loop(Rest, Fun, New_acc) + end. + +-file("src/gleam/list.gleam", 293). +?DOC( + " Returns a new list containing only the elements from the first list for\n" + " which the given functions returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert filter([2, 4, 6, 1], fn(x) { x > 2 }) == [4, 6]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert filter([2, 4, 6, 1], fn(x) { x > 6 }) == []\n" + " ```\n" +). +-spec filter(list(ABN), fun((ABN) -> boolean())) -> list(ABN). +filter(List, Predicate) -> + filter_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 327). +-spec filter_map_loop( + list(ACB), + fun((ACB) -> {ok, ACD} | {error, any()}), + list(ACD) +) -> list(ACD). +filter_map_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + New_acc = case Fun(First) of + {ok, First@1} -> + [First@1 | Acc]; + + {error, _} -> + Acc + end, + filter_map_loop(Rest, Fun, New_acc) + end. + +-file("src/gleam/list.gleam", 323). +?DOC( + " Returns a new list containing only the elements from the first list for\n" + " which the given functions returns `Ok(_)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert filter_map([2, 4, 6, 1], Error) == []\n" + " ```\n" + "\n" + " ```gleam\n" + " assert filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) == [3, 5, 7, 2]\n" + " ```\n" +). +-spec filter_map(list(ABU), fun((ABU) -> {ok, ABW} | {error, any()})) -> list(ABW). +filter_map(List, Fun) -> + filter_map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 356). +-spec map_loop(list(ACN), fun((ACN) -> ACP), list(ACP)) -> list(ACP). +map_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + map_loop(Rest, Fun, [Fun(First) | Acc]) + end. + +-file("src/gleam/list.gleam", 352). +?DOC( + " Returns a new list containing the results of applying the supplied function to each element.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert map([2, 4, 6], fn(x) { x * 2 }) == [4, 8, 12]\n" + " ```\n" +). +-spec map(list(ACJ), fun((ACJ) -> ACL)) -> list(ACL). +map(List, Fun) -> + map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 382). +-spec map2_loop(list(ACY), list(ADA), fun((ACY, ADA) -> ADC), list(ADC)) -> list(ADC). +map2_loop(List1, List2, Fun, Acc) -> + case {List1, List2} of + {[], _} -> + lists:reverse(Acc); + + {_, []} -> + lists:reverse(Acc); + + {[A | As_], [B | Bs]} -> + map2_loop(As_, Bs, Fun, [Fun(A, B) | Acc]) + end. + +-file("src/gleam/list.gleam", 378). +?DOC( + " Combines two lists into a single list using the given function.\n" + "\n" + " If a list is longer than the other, the extra elements are dropped.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y }) == [5, 7, 9]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert map2([1, 2], [\"a\", \"b\", \"c\"], fn(i, x) { #(i, x) })\n" + " == [#(1, \"a\"), #(2, \"b\")]\n" + " ```\n" +). +-spec map2(list(ACS), list(ACU), fun((ACS, ACU) -> ACW)) -> list(ACW). +map2(List1, List2, Fun) -> + map2_loop(List1, List2, Fun, []). + +-file("src/gleam/list.gleam", 416). +-spec map_fold_loop(list(ADK), fun((ADM, ADK) -> {ADM, ADN}), ADM, list(ADN)) -> {ADM, + list(ADN)}. +map_fold_loop(List, Fun, Acc, List_acc) -> + case List of + [] -> + {Acc, lists:reverse(List_acc)}; + + [First | Rest] -> + {Acc@1, First@1} = Fun(Acc, First), + map_fold_loop(Rest, Fun, Acc@1, [First@1 | List_acc]) + end. + +-file("src/gleam/list.gleam", 408). +?DOC( + " Similar to `map` but also lets you pass around an accumulated value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert\n" + " map_fold(\n" + " over: [1, 2, 3],\n" + " from: 100,\n" + " with: fn(memo, i) { #(memo + i, i * 2) }\n" + " )\n" + " == #(106, [2, 4, 6])\n" + " ```\n" +). +-spec map_fold(list(ADF), ADH, fun((ADH, ADF) -> {ADH, ADI})) -> {ADH, + list(ADI)}. +map_fold(List, Initial, Fun) -> + map_fold_loop(List, Fun, Initial, []). + +-file("src/gleam/list.gleam", 447). +-spec index_map_loop( + list(ADU), + fun((ADU, integer()) -> ADW), + integer(), + list(ADW) +) -> list(ADW). +index_map_loop(List, Fun, Index, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + Acc@1 = [Fun(First, Index) | Acc], + index_map_loop(Rest, Fun, Index + 1, Acc@1) + end. + +-file("src/gleam/list.gleam", 443). +?DOC( + " Similar to `map`, but the supplied function will also be passed the index\n" + " of the element being mapped as an additional argument.\n" + "\n" + " The index starts at 0, so the first element is 0, the second is 1, and so\n" + " on.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert index_map([\"a\", \"b\"], fn(x, i) { #(i, x) }) == [#(0, \"a\"), #(1, \"b\")]\n" + " ```\n" +). +-spec index_map(list(ADQ), fun((ADQ, integer()) -> ADS)) -> list(ADS). +index_map(List, Fun) -> + index_map_loop(List, Fun, 0, []). + +-file("src/gleam/list.gleam", 497). +-spec try_map_loop(list(AEI), fun((AEI) -> {ok, AEK} | {error, AEL}), list(AEK)) -> {ok, + list(AEK)} | + {error, AEL}. +try_map_loop(List, Fun, Acc) -> + case List of + [] -> + {ok, lists:reverse(Acc)}; + + [First | Rest] -> + case Fun(First) of + {ok, First@1} -> + try_map_loop(Rest, Fun, [First@1 | Acc]); + + {error, Error} -> + {error, Error} + end + end. + +-file("src/gleam/list.gleam", 490). +?DOC( + " Takes a function that returns a `Result` and applies it to each element in a\n" + " given list in turn.\n" + "\n" + " If the function returns `Ok(new_value)` for all elements in the list then a\n" + " list of the new values is returned.\n" + "\n" + " If the function returns `Error(reason)` for any of the elements then it is\n" + " returned immediately. None of the elements in the list are processed after\n" + " one returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert try_map([1, 2, 3], fn(x) { Ok(x + 2) }) == Ok([3, 4, 5])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert try_map([1, 2, 3], fn(_) { Error(0) }) == Error(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert try_map([[1], [2, 3]], first) == Ok([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert try_map([[1], [], [2]], first) == Error(Nil)\n" + " ```\n" +). +-spec try_map(list(ADZ), fun((ADZ) -> {ok, AEB} | {error, AEC})) -> {ok, + list(AEB)} | + {error, AEC}. +try_map(List, Fun) -> + try_map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 530). +?DOC( + " Returns a list that is the given list with up to the given number of\n" + " elements removed from the front of the list.\n" + "\n" + " If the list has less than the number of elements an empty list is\n" + " returned.\n" + "\n" + " This function runs in linear time but does not copy the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert drop([1, 2, 3, 4], 2) == [3, 4]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert drop([1, 2, 3, 4], 9) == []\n" + " ```\n" +). +-spec drop(list(AES), integer()) -> list(AES). +drop(List, N) -> + case N =< 0 of + true -> + List; + + false -> + case List of + [] -> + []; + + [_ | Rest] -> + drop(Rest, N - 1) + end + end. + +-file("src/gleam/list.gleam", 563). +-spec take_loop(list(AEY), integer(), list(AEY)) -> list(AEY). +take_loop(List, N, Acc) -> + case N =< 0 of + true -> + lists:reverse(Acc); + + false -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + take_loop(Rest, N - 1, [First | Acc]) + end + end. + +-file("src/gleam/list.gleam", 559). +?DOC( + " Returns a list containing the first given number of elements from the given\n" + " list.\n" + "\n" + " If the list has less than the number of elements then the full list is\n" + " returned.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert take([1, 2, 3, 4], 2) == [1, 2]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert take([1, 2, 3, 4], 9) == [1, 2, 3, 4]\n" + " ```\n" +). +-spec take(list(AEV), integer()) -> list(AEV). +take(List, N) -> + take_loop(List, N, []). + +-file("src/gleam/list.gleam", 582). +?DOC( + " Returns a new empty list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert new() == []\n" + " ```\n" +). +-spec new() -> list(any()). +new() -> + []. + +-file("src/gleam/list.gleam", 603). +?DOC( + " Returns the given item wrapped in a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert wrap(1) == [1]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert wrap([\"a\", \"b\", \"c\"]) == [[\"a\", \"b\", \"c\"]]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert wrap([[]]) == [[[]]]\n" + " ```\n" +). +-spec wrap(AFE) -> list(AFE). +wrap(Item) -> + [Item]. + +-file("src/gleam/list.gleam", 623). +-spec append_loop(list(AFK), list(AFK)) -> list(AFK). +append_loop(First, Second) -> + case First of + [] -> + Second; + + [First@1 | Rest] -> + append_loop(Rest, [First@1 | Second]) + end. + +-file("src/gleam/list.gleam", 619). +?DOC( + " Joins one list onto the end of another.\n" + "\n" + " This function runs in linear time, and it traverses and copies the first\n" + " list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert append([1, 2], [3]) == [1, 2, 3]\n" + " ```\n" +). +-spec append(list(AFG), list(AFG)) -> list(AFG). +append(First, Second) -> + lists:append(First, Second). + +-file("src/gleam/list.gleam", 643). +?DOC( + " Prefixes an item to a list. This can also be done using the dedicated\n" + " syntax instead.\n" + "\n" + " ```gleam\n" + " let existing_list = [2, 3, 4]\n" + " assert [1, ..existing_list] == [1, 2, 3, 4]\n" + " ```\n" + "\n" + " ```gleam\n" + " let existing_list = [2, 3, 4]\n" + " assert prepend(to: existing_list, this: 1) == [1, 2, 3, 4]\n" + " ```\n" +). +-spec prepend(list(AFO), AFO) -> list(AFO). +prepend(List, Item) -> + [Item | List]. + +-file("src/gleam/list.gleam", 663). +-spec flatten_loop(list(list(AFV)), list(AFV)) -> list(AFV). +flatten_loop(Lists, Acc) -> + case Lists of + [] -> + lists:reverse(Acc); + + [List | Further_lists] -> + flatten_loop(Further_lists, lists:reverse(List, Acc)) + end. + +-file("src/gleam/list.gleam", 659). +?DOC( + " Joins a list of lists into a single list.\n" + "\n" + " This function traverses all elements twice on the JavaScript target.\n" + " This function traverses all elements once on the Erlang target.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert flatten([[1], [2, 3], []]) == [1, 2, 3]\n" + " ```\n" +). +-spec flatten(list(list(AFR))) -> list(AFR). +flatten(Lists) -> + lists:append(Lists). + +-file("src/gleam/list.gleam", 679). +?DOC( + " Maps the list with the given function into a list of lists, and then flattens it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert flat_map([2, 4, 6], fn(x) { [x, x + 1] }) == [2, 3, 4, 5, 6, 7]\n" + " ```\n" +). +-spec flat_map(list(AGA), fun((AGA) -> list(AGC))) -> list(AGC). +flat_map(List, Fun) -> + lists:append(map(List, Fun)). + +-file("src/gleam/list.gleam", 691). +?DOC( + " Reduces a list of elements into a single value by calling a given function\n" + " on each element, going from left to right.\n" + "\n" + " `fold([1, 2, 3], 0, add)` is the equivalent of\n" + " `add(add(add(0, 1), 2), 3)`.\n" + "\n" + " This function runs in linear time.\n" +). +-spec fold(list(AGF), AGH, fun((AGH, AGF) -> AGH)) -> AGH. +fold(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + fold(Rest, Fun(Initial, First), Fun) + end. + +-file("src/gleam/list.gleam", 713). +?DOC( + " Reduces a list of elements into a single value by calling a given function\n" + " on each element, going from right to left.\n" + "\n" + " `fold_right([1, 2, 3], 0, add)` is the equivalent of\n" + " `add(add(add(0, 3), 2), 1)`.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " Unlike `fold` this function is not tail recursive. Where possible use\n" + " `fold` instead as it will use less memory.\n" +). +-spec fold_right(list(AGI), AGK, fun((AGK, AGI) -> AGK)) -> AGK. +fold_right(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + Fun(fold_right(Rest, Initial, Fun), First) + end. + +-file("src/gleam/list.gleam", 750). +-spec index_fold_loop( + list(AGO), + AGQ, + fun((AGQ, AGO, integer()) -> AGQ), + integer() +) -> AGQ. +index_fold_loop(Over, Acc, With, Index) -> + case Over of + [] -> + Acc; + + [First | Rest] -> + index_fold_loop(Rest, With(Acc, First, Index), With, Index + 1) + end. + +-file("src/gleam/list.gleam", 742). +?DOC( + " Like `fold` but the folding function also receives the index of the current element.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [\"a\", \"b\", \"c\"]\n" + " |> index_fold(\"\", fn(acc, item, index) {\n" + " acc <> int.to_string(index) <> \":\" <> item <> \" \"\n" + " })\n" + " == \"0:a 1:b 2:c\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert [10, 20, 30]\n" + " |> index_fold(0, fn(acc, item, index) { acc + item * index })\n" + " == 80\n" + " ```\n" +). +-spec index_fold(list(AGL), AGN, fun((AGN, AGL, integer()) -> AGN)) -> AGN. +index_fold(List, Initial, Fun) -> + index_fold_loop(List, Initial, Fun, 0). + +-file("src/gleam/list.gleam", 782). +?DOC( + " A variant of fold that might fail.\n" + "\n" + " The folding function should return `Result(accumulator, error)`.\n" + " If the returned value is `Ok(accumulator)` try_fold will try the next value in the list.\n" + " If the returned value is `Error(error)` try_fold will stop and return that error.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [1, 2, 3, 4]\n" + " |> try_fold(0, fn(acc, i) {\n" + " case i < 3 {\n" + " True -> Ok(acc + i)\n" + " False -> Error(Nil)\n" + " }\n" + " })\n" + " == Error(Nil)\n" + " ```\n" +). +-spec try_fold(list(AGR), AGT, fun((AGT, AGR) -> {ok, AGT} | {error, AGU})) -> {ok, + AGT} | + {error, AGU}. +try_fold(List, Initial, Fun) -> + case List of + [] -> + {ok, Initial}; + + [First | Rest] -> + case Fun(Initial, First) of + {ok, Result} -> + try_fold(Rest, Result, Fun); + + {error, _} = Error -> + Error + end + end. + +-file("src/gleam/list.gleam", 821). +?DOC( + " A variant of fold that allows to stop folding earlier.\n" + "\n" + " The folding function should return `ContinueOrStop(accumulator)`.\n" + " If the returned value is `Continue(accumulator)` fold_until will try the next value in the list.\n" + " If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [1, 2, 3, 4]\n" + " |> fold_until(0, fn(acc, i) {\n" + " case i < 3 {\n" + " True -> Continue(acc + i)\n" + " False -> Stop(acc)\n" + " }\n" + " })\n" + " == 3\n" + " ```\n" +). +-spec fold_until(list(AGZ), AHB, fun((AHB, AGZ) -> continue_or_stop(AHB))) -> AHB. +fold_until(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + case Fun(Initial, First) of + {continue, Next_accumulator} -> + fold_until(Rest, Next_accumulator, Fun); + + {stop, B} -> + B + end + end. + +-file("src/gleam/list.gleam", 855). +?DOC( + " Finds the first element in a given list for which the given function returns\n" + " `True`.\n" + "\n" + " Returns `Error(Nil)` if no such element is found.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert find([1, 2, 3], fn(x) { x > 2 }) == Ok(3)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert find([1, 2, 3], fn(x) { x > 4 }) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert find([], fn(_) { True }) == Error(Nil)\n" + " ```\n" +). +-spec find(list(AHD), fun((AHD) -> boolean())) -> {ok, AHD} | {error, nil}. +find(List, Is_desired) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + case Is_desired(First) of + true -> + {ok, First}; + + false -> + find(Rest, Is_desired) + end + end. + +-file("src/gleam/list.gleam", 888). +?DOC( + " Finds the first element in a given list for which the given function returns\n" + " `Ok(new_value)`, then returns the wrapped `new_value`.\n" + "\n" + " Returns `Error(Nil)` if no such element is found.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert find_map([[], [2], [3]], first) == Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert find_map([[], []], first) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert find_map([], first) == Error(Nil)\n" + " ```\n" +). +-spec find_map(list(AHH), fun((AHH) -> {ok, AHJ} | {error, any()})) -> {ok, AHJ} | + {error, nil}. +find_map(List, Fun) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + case Fun(First) of + {ok, First@1} -> + {ok, First@1}; + + {error, _} -> + find_map(Rest, Fun) + end + end. + +-file("src/gleam/list.gleam", 920). +?DOC( + " Returns `True` if the given function returns `True` for all the elements in\n" + " the given list. If the function returns `False` for any of the elements it\n" + " immediately returns `False` without checking the rest of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert all([], fn(x) { x > 3 })\n" + " ```\n" + "\n" + " ```gleam\n" + " assert all([4, 5], fn(x) { x > 3 })\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !all([4, 3], fn(x) { x > 3 })\n" + " ```\n" +). +-spec all(list(AHP), fun((AHP) -> boolean())) -> boolean(). +all(List, Predicate) -> + case List of + [] -> + true; + + [First | Rest] -> + case Predicate(First) of + true -> + all(Rest, Predicate); + + false -> + false + end + end. + +-file("src/gleam/list.gleam", 953). +?DOC( + " Returns `True` if the given function returns `True` for any the elements in\n" + " the given list. If the function returns `True` for any of the elements it\n" + " immediately returns `True` without checking the rest of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert !any([], fn(x) { x > 3 })\n" + " ```\n" + "\n" + " ```gleam\n" + " assert any([4, 5], fn(x) { x > 3 })\n" + " ```\n" + "\n" + " ```gleam\n" + " assert any([4, 3], fn(x) { x > 4 })\n" + " ```\n" + "\n" + " ```gleam\n" + " assert any([3, 4], fn(x) { x > 3 })\n" + " ```\n" +). +-spec any(list(AHR), fun((AHR) -> boolean())) -> boolean(). +any(List, Predicate) -> + case List of + [] -> + false; + + [First | Rest] -> + case Predicate(First) of + true -> + true; + + false -> + any(Rest, Predicate) + end + end. + +-file("src/gleam/list.gleam", 991). +-spec zip_loop(list(AHY), list(AIA), list({AHY, AIA})) -> list({AHY, AIA}). +zip_loop(One, Other, Acc) -> + case {One, Other} of + {[First_one | Rest_one], [First_other | Rest_other]} -> + zip_loop(Rest_one, Rest_other, [{First_one, First_other} | Acc]); + + {_, _} -> + lists:reverse(Acc) + end. + +-file("src/gleam/list.gleam", 987). +?DOC( + " Takes two lists and returns a single list of 2-element tuples.\n" + "\n" + " If one of the lists is longer than the other, the remaining elements from\n" + " the longer list are not used.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert zip([], []) == []\n" + " ```\n" + "\n" + " ```gleam\n" + " assert zip([1, 2], [3]) == [#(1, 3)]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert zip([1], [3, 4]) == [#(1, 3)]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert zip([1, 2], [3, 4]) == [#(1, 3), #(2, 4)]\n" + " ```\n" +). +-spec zip(list(AHT), list(AHV)) -> list({AHT, AHV}). +zip(List, Other) -> + zip_loop(List, Other, []). + +-file("src/gleam/list.gleam", 1028). +-spec strict_zip_loop(list(AIL), list(AIN), list({AIL, AIN})) -> {ok, + list({AIL, AIN})} | + {error, nil}. +strict_zip_loop(One, Other, Acc) -> + case {One, Other} of + {[], []} -> + {ok, lists:reverse(Acc)}; + + {[], _} -> + {error, nil}; + + {_, []} -> + {error, nil}; + + {[First_one | Rest_one], [First_other | Rest_other]} -> + strict_zip_loop( + Rest_one, + Rest_other, + [{First_one, First_other} | Acc] + ) + end. + +-file("src/gleam/list.gleam", 1021). +?DOC( + " Takes two lists and returns a single list of 2-element tuples.\n" + "\n" + " If one of the lists is longer than the other, an `Error` is returned.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert strict_zip([], []) == Ok([])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert strict_zip([1, 2], [3]) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert strict_zip([1], [3, 4]) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert strict_zip([1, 2], [3, 4]) == Ok([#(1, 3), #(2, 4)])\n" + " ```\n" +). +-spec strict_zip(list(AIE), list(AIG)) -> {ok, list({AIE, AIG})} | {error, nil}. +strict_zip(List, Other) -> + strict_zip_loop(List, Other, []). + +-file("src/gleam/list.gleam", 1057). +-spec unzip_loop(list({AIY, AIZ}), list(AIY), list(AIZ)) -> {list(AIY), + list(AIZ)}. +unzip_loop(Input, One, Other) -> + case Input of + [] -> + {lists:reverse(One), lists:reverse(Other)}; + + [{First_one, First_other} | Rest] -> + unzip_loop(Rest, [First_one | One], [First_other | Other]) + end. + +-file("src/gleam/list.gleam", 1053). +?DOC( + " Takes a single list of 2-element tuples and returns two lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert unzip([#(1, 2), #(3, 4)]) == #([1, 3], [2, 4])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert unzip([]) == #([], [])\n" + " ```\n" +). +-spec unzip(list({AIT, AIU})) -> {list(AIT), list(AIU)}. +unzip(Input) -> + unzip_loop(Input, [], []). + +-file("src/gleam/list.gleam", 1090). +-spec intersperse_loop(list(AJI), AJI, list(AJI)) -> list(AJI). +intersperse_loop(List, Separator, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + intersperse_loop(Rest, Separator, [First, Separator | Acc]) + end. + +-file("src/gleam/list.gleam", 1083). +?DOC( + " Inserts a given value between each existing element in a given list.\n" + "\n" + " This function runs in linear time and copies the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert intersperse([1, 1, 1], 2) == [1, 2, 1, 2, 1]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert intersperse([], 2) == []\n" + " ```\n" +). +-spec intersperse(list(AJF), AJF) -> list(AJF). +intersperse(List, Elem) -> + case List of + [] -> + List; + + [_] -> + List; + + [First | Rest] -> + intersperse_loop(Rest, Elem, [First]) + end. + +-file("src/gleam/list.gleam", 1112). +-spec unique_loop(list(AJP), gleam@dict:dict(AJP, nil), list(AJP)) -> list(AJP). +unique_loop(List, Seen, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + case gleam@dict:has_key(Seen, First) of + true -> + unique_loop(Rest, Seen, Acc); + + false -> + unique_loop( + Rest, + gleam@dict:insert(Seen, First, nil), + [First | Acc] + ) + end + end. + +-file("src/gleam/list.gleam", 1108). +?DOC( + " Removes any duplicate elements from a given list.\n" + "\n" + " This function returns in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert unique([1, 1, 1, 4, 7, 3, 3, 4]) == [1, 4, 7, 3]\n" + " ```\n" +). +-spec unique(list(AJM)) -> list(AJM). +unique(List) -> + unique_loop(List, maps:new(), []). + +-file("src/gleam/list.gleam", 1372). +?DOC( + " This is exactly the same as merge_ascendings but mirrored: it merges two\n" + " lists sorted in descending order into a single list sorted in ascending\n" + " order according to the given comparator function.\n" + "\n" + " This reversing of the sort order is not avoidable if we want to implement\n" + " merge as a tail recursive function. We could reverse the accumulator before\n" + " returning it but that would end up being less efficient; so the merging\n" + " algorithm has to play around this.\n" +). +-spec merge_descendings( + list(ALA), + list(ALA), + fun((ALA, ALA) -> gleam@order:order()), + list(ALA) +) -> list(ALA). +merge_descendings(List1, List2, Compare, Acc) -> + case {List1, List2} of + {[], List} -> + lists:reverse(List, Acc); + + {List, []} -> + lists:reverse(List, Acc); + + {[First1 | Rest1], [First2 | Rest2]} -> + case Compare(First1, First2) of + lt -> + merge_descendings(List1, Rest2, Compare, [First2 | Acc]); + + gt -> + merge_descendings(Rest1, List2, Compare, [First1 | Acc]); + + eq -> + merge_descendings(Rest1, List2, Compare, [First1 | Acc]) + end + end. + +-file("src/gleam/list.gleam", 1320). +?DOC(" This is the same as merge_ascending_pairs but flipped for descending lists.\n"). +-spec merge_descending_pairs( + list(list(AKP)), + fun((AKP, AKP) -> gleam@order:order()), + list(list(AKP)) +) -> list(list(AKP)). +merge_descending_pairs(Sequences, Compare, Acc) -> + case Sequences of + [] -> + lists:reverse(Acc); + + [Sequence] -> + lists:reverse([lists:reverse(Sequence) | Acc]); + + [Descending1, Descending2 | Rest] -> + Ascending = merge_descendings(Descending1, Descending2, Compare, []), + merge_descending_pairs(Rest, Compare, [Ascending | Acc]) + end. + +-file("src/gleam/list.gleam", 1345). +?DOC( + " Merges two lists sorted in ascending order into a single list sorted in\n" + " descending order according to the given comparator function.\n" + "\n" + " This reversing of the sort order is not avoidable if we want to implement\n" + " merge as a tail recursive function. We could reverse the accumulator before\n" + " returning it but that would end up being less efficient; so the merging\n" + " algorithm has to play around this.\n" +). +-spec merge_ascendings( + list(AKV), + list(AKV), + fun((AKV, AKV) -> gleam@order:order()), + list(AKV) +) -> list(AKV). +merge_ascendings(List1, List2, Compare, Acc) -> + case {List1, List2} of + {[], List} -> + lists:reverse(List, Acc); + + {List, []} -> + lists:reverse(List, Acc); + + {[First1 | Rest1], [First2 | Rest2]} -> + case Compare(First1, First2) of + lt -> + merge_ascendings(Rest1, List2, Compare, [First1 | Acc]); + + gt -> + merge_ascendings(List1, Rest2, Compare, [First2 | Acc]); + + eq -> + merge_ascendings(List1, Rest2, Compare, [First2 | Acc]) + end + end. + +-file("src/gleam/list.gleam", 1298). +?DOC( + " Given a list of ascending lists, it merges adjacent pairs into a single\n" + " descending list, halving their number.\n" + " It returns a list of the remaining descending lists.\n" +). +-spec merge_ascending_pairs( + list(list(AKJ)), + fun((AKJ, AKJ) -> gleam@order:order()), + list(list(AKJ)) +) -> list(list(AKJ)). +merge_ascending_pairs(Sequences, Compare, Acc) -> + case Sequences of + [] -> + lists:reverse(Acc); + + [Sequence] -> + lists:reverse([lists:reverse(Sequence) | Acc]); + + [Ascending1, Ascending2 | Rest] -> + Descending = merge_ascendings(Ascending1, Ascending2, Compare, []), + merge_ascending_pairs(Rest, Compare, [Descending | Acc]) + end. + +-file("src/gleam/list.gleam", 1264). +?DOC( + " Given some some sorted sequences (assumed to be sorted in `direction`) it\n" + " merges them all together until we're left with just a list sorted in\n" + " ascending order.\n" +). +-spec merge_all( + list(list(AKF)), + sorting(), + fun((AKF, AKF) -> gleam@order:order()) +) -> list(AKF). +merge_all(Sequences, Direction, Compare) -> + case {Sequences, Direction} of + {[], _} -> + []; + + {[Sequence], ascending} -> + Sequence; + + {[Sequence@1], descending} -> + lists:reverse(Sequence@1); + + {_, ascending} -> + Sequences@1 = merge_ascending_pairs(Sequences, Compare, []), + merge_all(Sequences@1, descending, Compare); + + {_, descending} -> + Sequences@2 = merge_descending_pairs(Sequences, Compare, []), + merge_all(Sequences@2, ascending, Compare) + end. + +-file("src/gleam/list.gleam", 1197). +?DOC( + " Given a list it returns slices of it that are locally sorted in ascending\n" + " order.\n" + "\n" + " Imagine you have this list:\n" + "\n" + " ```\n" + " [1, 2, 3, 2, 1, 0]\n" + " ^^^^^^^ ^^^^^^^ This is a slice in descending order\n" + " |\n" + " | This is a slice that is sorted in ascending order\n" + " ```\n" + "\n" + " So the produced result will contain these two slices, each one sorted in\n" + " ascending order: `[[1, 2, 3], [0, 1, 2]]`.\n" + "\n" + " - `growing` is an accumulator with the current slice being grown\n" + " - `direction` is the growing direction of the slice being grown, it could\n" + " either be ascending or strictly descending\n" + " - `prev` is the previous element that needs to be added to the growing slice\n" + " it is carried around to check whether we have to keep growing the current\n" + " slice or not\n" + " - `acc` is the accumulator containing the slices sorted in ascending order\n" +). +-spec sequences( + list(AJY), + fun((AJY, AJY) -> gleam@order:order()), + list(AJY), + sorting(), + AJY, + list(list(AJY)) +) -> list(list(AJY)). +sequences(List, Compare, Growing, Direction, Prev, Acc) -> + Growing@1 = [Prev | Growing], + case List of + [] -> + case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end; + + [New | Rest] -> + case {Compare(Prev, New), Direction} of + {gt, descending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {lt, ascending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {eq, ascending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {gt, ascending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end; + + {lt, descending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end; + + {eq, descending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end + end + end. + +-file("src/gleam/list.gleam", 1135). +?DOC( + " Sorts from smallest to largest based upon the ordering specified by a given\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " assert sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) == [1, 2, 3, 4, 4, 5, 6]\n" + " ```\n" +). +-spec sort(list(AJV), fun((AJV, AJV) -> gleam@order:order())) -> list(AJV). +sort(List, Compare) -> + case List of + [] -> + []; + + [X] -> + [X]; + + [X@1, Y | Rest] -> + Direction = case Compare(X@1, Y) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + Sequences = sequences(Rest, Compare, [X@1], Direction, Y, []), + merge_all(Sequences, ascending, Compare) + end. + +-file("src/gleam/list.gleam", 1405). +-spec repeat_loop(ALH, integer(), list(ALH)) -> list(ALH). +repeat_loop(Item, Times, Acc) -> + case Times =< 0 of + true -> + Acc; + + false -> + repeat_loop(Item, Times - 1, [Item | Acc]) + end. + +-file("src/gleam/list.gleam", 1401). +?DOC( + " Builds a list of a given value a given number of times.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert repeat(\"a\", times: 0) == []\n" + " ```\n" + "\n" + " ```gleam\n" + " assert repeat(\"a\", times: 5) == [\"a\", \"a\", \"a\", \"a\", \"a\"]\n" + " ```\n" +). +-spec repeat(ALF, integer()) -> list(ALF). +repeat(A, Times) -> + repeat_loop(A, Times, []). + +-file("src/gleam/list.gleam", 1435). +-spec split_loop(list(ALO), integer(), list(ALO)) -> {list(ALO), list(ALO)}. +split_loop(List, N, Taken) -> + case N =< 0 of + true -> + {lists:reverse(Taken), List}; + + false -> + case List of + [] -> + {lists:reverse(Taken), []}; + + [First | Rest] -> + split_loop(Rest, N - 1, [First | Taken]) + end + end. + +-file("src/gleam/list.gleam", 1431). +?DOC( + " Splits a list in two before the given index.\n" + "\n" + " If the list is not long enough to have the given index the before list will\n" + " be the input list, and the after list will be empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert split([6, 7, 8, 9], 0) == #([], [6, 7, 8, 9])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert split([6, 7, 8, 9], 2) == #([6, 7], [8, 9])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert split([6, 7, 8, 9], 4) == #([6, 7, 8, 9], [])\n" + " ```\n" +). +-spec split(list(ALK), integer()) -> {list(ALK), list(ALK)}. +split(List, Index) -> + split_loop(List, Index, []). + +-file("src/gleam/list.gleam", 1471). +-spec split_while_loop(list(ALX), fun((ALX) -> boolean()), list(ALX)) -> {list(ALX), + list(ALX)}. +split_while_loop(List, F, Acc) -> + case List of + [] -> + {lists:reverse(Acc), []}; + + [First | Rest] -> + case F(First) of + true -> + split_while_loop(Rest, F, [First | Acc]); + + false -> + {lists:reverse(Acc), List} + end + end. + +-file("src/gleam/list.gleam", 1464). +?DOC( + " Splits a list in two before the first element that a given function returns\n" + " `False` for.\n" + "\n" + " If the function returns `True` for all elements the first list will be the\n" + " input list, and the second list will be empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 })\n" + " == #([1, 2, 3], [4, 5])\n" + " ```\n" + "\n" + " ```gleam\n" + " assert split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })\n" + " == #([1, 2, 3, 4, 5], [])\n" + " ```\n" +). +-spec split_while(list(ALT), fun((ALT) -> boolean())) -> {list(ALT), list(ALT)}. +split_while(List, Predicate) -> + split_while_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 1508). +?DOC( + " Given a list of 2-element tuples, finds the first tuple that has a given\n" + " key as the first element and returns the second element.\n" + "\n" + " If no tuple is found with the given key then `Error(Nil)` is returned.\n" + "\n" + " This function may be useful for interacting with Erlang code where lists of\n" + " tuples are common.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert key_find([#(\"a\", 0), #(\"b\", 1)], \"a\") == Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert key_find([#(\"a\", 0), #(\"b\", 1)], \"b\") == Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert key_find([#(\"a\", 0), #(\"b\", 1)], \"c\") == Error(Nil)\n" + " ```\n" +). +-spec key_find(list({AMC, AMD}), AMC) -> {ok, AMD} | {error, nil}. +key_find(Keyword_list, Desired_key) -> + find_map( + Keyword_list, + fun(Keyword) -> + {Key, Value} = Keyword, + case Key =:= Desired_key of + true -> + {ok, Value}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/list.gleam", 1537). +?DOC( + " Given a list of 2-element tuples, finds all tuples that have a given\n" + " key as the first element and returns the second element.\n" + "\n" + " This function may be useful for interacting with Erlang code where lists of\n" + " tuples are common.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert key_filter([#(\"a\", 0), #(\"b\", 1), #(\"a\", 2)], \"a\") == [0, 2]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert key_filter([#(\"a\", 0), #(\"b\", 1)], \"c\") == []\n" + " ```\n" +). +-spec key_filter(list({AMH, AMI}), AMH) -> list(AMI). +key_filter(Keyword_list, Desired_key) -> + filter_map( + Keyword_list, + fun(Keyword) -> + {Key, Value} = Keyword, + case Key =:= Desired_key of + true -> + {ok, Value}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/list.gleam", 1574). +-spec key_pop_loop(list({AMR, AMS}), AMR, list({AMR, AMS})) -> {ok, + {AMS, list({AMR, AMS})}} | + {error, nil}. +key_pop_loop(List, Key, Checked) -> + case List of + [] -> + {error, nil}; + + [{K, V} | Rest] when K =:= Key -> + {ok, {V, lists:reverse(Checked, Rest)}}; + + [First | Rest@1] -> + key_pop_loop(Rest@1, Key, [First | Checked]) + end. + +-file("src/gleam/list.gleam", 1570). +?DOC( + " Given a list of 2-element tuples, finds the first tuple that has a given\n" + " key as the first element. This function will return the second element\n" + " of the found tuple and list with tuple removed.\n" + "\n" + " If no tuple is found with the given key then `Error(Nil)` is returned.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert key_pop([#(\"a\", 0), #(\"b\", 1)], \"a\") == Ok(#(0, [#(\"b\", 1)]))\n" + " ```\n" + "\n" + " ```gleam\n" + " assert key_pop([#(\"a\", 0), #(\"b\", 1)], \"b\") == Ok(#(1, [#(\"a\", 0)]))\n" + " ```\n" + "\n" + " ```gleam\n" + " assert key_pop([#(\"a\", 0), #(\"b\", 1)], \"c\") == Error(Nil)\n" + " ```\n" +). +-spec key_pop(list({AML, AMM}), AML) -> {ok, {AMM, list({AML, AMM})}} | + {error, nil}. +key_pop(List, Key) -> + key_pop_loop(List, Key, []). + +-file("src/gleam/list.gleam", 1606). +-spec key_set_loop(list({ANC, AND}), ANC, AND, list({ANC, AND})) -> list({ANC, + AND}). +key_set_loop(List, Key, Value, Inspected) -> + case List of + [{K, _} | Rest] when K =:= Key -> + lists:reverse(Inspected, [{K, Value} | Rest]); + + [First | Rest@1] -> + key_set_loop(Rest@1, Key, Value, [First | Inspected]); + + [] -> + lists:reverse([{Key, Value} | Inspected]) + end. + +-file("src/gleam/list.gleam", 1602). +?DOC( + " Given a list of 2-element tuples, inserts a key and value into the list.\n" + "\n" + " If there was already a tuple with the key then it is replaced, otherwise it\n" + " is added to the end of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert key_set([#(5, 0), #(4, 1)], 4, 100) == [#(5, 0), #(4, 100)]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert key_set([#(5, 0), #(4, 1)], 1, 100) == [#(5, 0), #(4, 1), #(1, 100)]\n" + " ```\n" +). +-spec key_set(list({AMY, AMZ}), AMY, AMZ) -> list({AMY, AMZ}). +key_set(List, Key, Value) -> + key_set_loop(List, Key, Value, []). + +-file("src/gleam/list.gleam", 1633). +?DOC( + " Calls a function for each element in a list, discarding the return value.\n" + "\n" + " Useful for calling a side effect for every item of a list.\n" + "\n" + " ```gleam\n" + " import gleam/io\n" + "\n" + " assert each([\"1\", \"2\", \"3\"], io.println) == Nil\n" + " // 1\n" + " // 2\n" + " // 3\n" + " ```\n" +). +-spec each(list(ANH), fun((ANH) -> any())) -> nil. +each(List, F) -> + case List of + [] -> + nil; + + [First | Rest] -> + F(First), + each(Rest, F) + end. + +-file("src/gleam/list.gleam", 1660). +?DOC( + " Calls a `Result` returning function for each element in a list, discarding\n" + " the return value. If the function returns `Error` then the iteration is\n" + " stopped and the error is returned.\n" + "\n" + " Useful for calling a side effect for every item of a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert\n" + " try_each(\n" + " over: [1, 2, 3],\n" + " with: function_that_might_fail,\n" + " )\n" + " == Ok(Nil)\n" + " ```\n" +). +-spec try_each(list(ANK), fun((ANK) -> {ok, any()} | {error, ANN})) -> {ok, nil} | + {error, ANN}. +try_each(List, Fun) -> + case List of + [] -> + {ok, nil}; + + [First | Rest] -> + case Fun(First) of + {ok, _} -> + try_each(Rest, Fun); + + {error, E} -> + {error, E} + end + end. + +-file("src/gleam/list.gleam", 1692). +-spec partition_loop(list(BGI), fun((BGI) -> boolean()), list(BGI), list(BGI)) -> {list(BGI), + list(BGI)}. +partition_loop(List, Categorise, Trues, Falses) -> + case List of + [] -> + {lists:reverse(Trues), lists:reverse(Falses)}; + + [First | Rest] -> + case Categorise(First) of + true -> + partition_loop(Rest, Categorise, [First | Trues], Falses); + + false -> + partition_loop(Rest, Categorise, Trues, [First | Falses]) + end + end. + +-file("src/gleam/list.gleam", 1685). +?DOC( + " Partitions a list into a tuple/pair of lists\n" + " by a given categorisation function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " assert [1, 2, 3, 4, 5] |> partition(int.is_odd) == #([1, 3, 5], [2, 4])\n" + " ```\n" +). +-spec partition(list(ANS), fun((ANS) -> boolean())) -> {list(ANS), list(ANS)}. +partition(List, Categorise) -> + partition_loop(List, Categorise, [], []). + +-file("src/gleam/list.gleam", 1736). +-spec permutation_prepend( + AOM, + list(list(AOM)), + list(AOM), + list(AOM), + list(list(AOM)) +) -> list(list(AOM)). +permutation_prepend(El, Permutations, List_1, List_2, Acc) -> + case Permutations of + [] -> + permutation_zip(List_1, List_2, Acc); + + [Head | Tail] -> + permutation_prepend(El, Tail, List_1, List_2, [[El | Head] | Acc]) + end. + +-file("src/gleam/list.gleam", 1718). +-spec permutation_zip(list(AOF), list(AOF), list(list(AOF))) -> list(list(AOF)). +permutation_zip(List, Rest, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [Head | Tail] -> + permutation_prepend( + Head, + permutations(lists:reverse(Rest, Tail)), + Tail, + [Head | Rest], + Acc + ) + end. + +-file("src/gleam/list.gleam", 1711). +?DOC( + " Returns all the permutations of a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert permutations([1, 2]) == [[1, 2], [2, 1]]\n" + " ```\n" +). +-spec permutations(list(AOB)) -> list(list(AOB)). +permutations(List) -> + case List of + [] -> + [[]]; + + L -> + permutation_zip(L, [], []) + end. + +-file("src/gleam/list.gleam", 1769). +-spec window_loop(list(list(AOZ)), list(AOZ), integer()) -> list(list(AOZ)). +window_loop(Acc, List, N) -> + Window = take(List, N), + case erlang:length(Window) =:= N of + true -> + window_loop([Window | Acc], drop(List, 1), N); + + false -> + lists:reverse(Acc) + end. + +-file("src/gleam/list.gleam", 1762). +?DOC( + " Returns a list of sliding windows.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert window([1,2,3,4,5], 3) == [[1, 2, 3], [2, 3, 4], [3, 4, 5]]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert window([1, 2], 4) == []\n" + " ```\n" +). +-spec window(list(AOV), integer()) -> list(list(AOV)). +window(List, N) -> + case N =< 0 of + true -> + []; + + false -> + window_loop([], List, N) + end. + +-file("src/gleam/list.gleam", 1790). +?DOC( + " Returns a list of tuples containing two contiguous elements.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert window_by_2([1,2,3,4]) == [#(1, 2), #(2, 3), #(3, 4)]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert window_by_2([1]) == []\n" + " ```\n" +). +-spec window_by_2(list(APF)) -> list({APF, APF}). +window_by_2(List) -> + zip(List, drop(List, 1)). + +-file("src/gleam/list.gleam", 1802). +?DOC( + " Drops the first elements in a given list for which the predicate function returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert drop_while([1, 2, 3, 4], fn (x) { x < 3 }) == [3, 4]\n" + " ```\n" +). +-spec drop_while(list(API), fun((API) -> boolean())) -> list(API). +drop_while(List, Predicate) -> + case List of + [] -> + []; + + [First | Rest] -> + case Predicate(First) of + true -> + drop_while(Rest, Predicate); + + false -> + [First | Rest] + end + end. + +-file("src/gleam/list.gleam", 1831). +-spec take_while_loop(list(APO), fun((APO) -> boolean()), list(APO)) -> list(APO). +take_while_loop(List, Predicate, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + case Predicate(First) of + true -> + take_while_loop(Rest, Predicate, [First | Acc]); + + false -> + lists:reverse(Acc) + end + end. + +-file("src/gleam/list.gleam", 1824). +?DOC( + " Takes the first elements in a given list for which the predicate function returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert take_while([1, 2, 3, 2, 4], fn (x) { x < 3 }) == [1, 2]\n" + " ```\n" +). +-spec take_while(list(APL), fun((APL) -> boolean())) -> list(APL). +take_while(List, Predicate) -> + take_while_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 1863). +-spec chunk_loop(list(APX), fun((APX) -> APZ), APZ, list(APX), list(list(APX))) -> list(list(APX)). +chunk_loop(List, F, Previous_key, Current_chunk, Acc) -> + case List of + [First | Rest] -> + Key = F(First), + case Key =:= Previous_key of + true -> + chunk_loop(Rest, F, Key, [First | Current_chunk], Acc); + + false -> + New_acc = [lists:reverse(Current_chunk) | Acc], + chunk_loop(Rest, F, Key, [First], New_acc) + end; + + [] -> + lists:reverse([lists:reverse(Current_chunk) | Acc]) + end. + +-file("src/gleam/list.gleam", 1856). +?DOC( + " Returns a list of chunks in which\n" + " the return value of calling `f` on each element is the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })\n" + " == [[1], [2, 2], [3], [4, 4, 6], [7, 7]]\n" + " ```\n" +). +-spec chunk(list(APS), fun((APS) -> any())) -> list(list(APS)). +chunk(List, F) -> + case List of + [] -> + []; + + [First | Rest] -> + chunk_loop(Rest, F, F(First), [First], []) + end. + +-file("src/gleam/list.gleam", 1908). +-spec sized_chunk_loop( + list(AQJ), + integer(), + integer(), + list(AQJ), + list(list(AQJ)) +) -> list(list(AQJ)). +sized_chunk_loop(List, Count, Left, Current_chunk, Acc) -> + case List of + [] -> + case Current_chunk of + [] -> + lists:reverse(Acc); + + Remaining -> + lists:reverse([lists:reverse(Remaining) | Acc]) + end; + + [First | Rest] -> + Chunk = [First | Current_chunk], + case Left > 1 of + true -> + sized_chunk_loop(Rest, Count, Left - 1, Chunk, Acc); + + false -> + sized_chunk_loop( + Rest, + Count, + Count, + [], + [lists:reverse(Chunk) | Acc] + ) + end + end. + +-file("src/gleam/list.gleam", 1904). +?DOC( + " Returns a list of chunks containing `count` elements each.\n" + "\n" + " If the last chunk does not have `count` elements, it is instead\n" + " a partial chunk, with less than `count` elements.\n" + "\n" + " For any `count` less than 1 this function behaves as if it was set to 1.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2)\n" + " == [[1, 2], [3, 4], [5, 6]]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3)\n" + " == [[1, 2, 3], [4, 5, 6], [7, 8]]\n" + " ```\n" +). +-spec sized_chunk(list(AQF), integer()) -> list(list(AQF)). +sized_chunk(List, Count) -> + sized_chunk_loop(List, Count, Count, [], []). + +-file("src/gleam/list.gleam", 1950). +?DOC( + " This function acts similar to fold, but does not take an initial state.\n" + " Instead, it starts from the first element in the list\n" + " and combines it with each subsequent element in turn using the given\n" + " function. The function is called as `fun(accumulator, current_element)`.\n" + "\n" + " Returns `Ok` to indicate a successful run, and `Error` if called on an\n" + " empty list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [] |> reduce(fn(acc, x) { acc + x }) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x }) == Ok(15)\n" + " ```\n" +). +-spec reduce(list(AQQ), fun((AQQ, AQQ) -> AQQ)) -> {ok, AQQ} | {error, nil}. +reduce(List, Fun) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + {ok, fold(Rest, First, Fun)} + end. + +-file("src/gleam/list.gleam", 1974). +-spec scan_loop(list(AQY), ARA, list(ARA), fun((ARA, AQY) -> ARA)) -> list(ARA). +scan_loop(List, Accumulator, Accumulated, Fun) -> + case List of + [] -> + lists:reverse(Accumulated); + + [First | Rest] -> + Next = Fun(Accumulator, First), + scan_loop(Rest, Next, [Next | Accumulated], Fun) + end. + +-file("src/gleam/list.gleam", 1966). +?DOC( + " Similar to `fold`, but yields the state of the accumulator at each stage.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i })\n" + " == [101, 103, 106]\n" + " ```\n" +). +-spec scan(list(AQU), AQW, fun((AQW, AQU) -> AQW)) -> list(AQW). +scan(List, Initial, Fun) -> + scan_loop(List, Initial, [], Fun). + +-file("src/gleam/list.gleam", 2005). +?DOC( + " Returns the last element in the given list.\n" + "\n" + " Returns `Error(Nil)` if the list is empty.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert last([]) == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert last([1, 2, 3, 4, 5]) == Ok(5)\n" + " ```\n" +). +-spec last(list(ARD)) -> {ok, ARD} | {error, nil}. +last(List) -> + case List of + [] -> + {error, nil}; + + [Last] -> + {ok, Last}; + + [_ | Rest] -> + last(Rest) + end. + +-file("src/gleam/list.gleam", 2026). +?DOC( + " Return unique combinations of elements in the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert combinations([1, 2, 3], 2) == [[1, 2], [1, 3], [2, 3]]\n" + " ```\n" + "\n" + " ```gleam\n" + " assert combinations([1, 2, 3, 4], 3)\n" + " == [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]\n" + " ```\n" +). +-spec combinations(list(ARH), integer()) -> list(list(ARH)). +combinations(Items, N) -> + case {N, Items} of + {0, _} -> + [[]]; + + {_, []} -> + []; + + {_, [First | Rest]} -> + _pipe = Rest, + _pipe@1 = combinations(_pipe, N - 1), + _pipe@2 = map( + _pipe@1, + fun(Combination) -> [First | Combination] end + ), + _pipe@3 = lists:reverse(_pipe@2), + fold(_pipe@3, combinations(Rest, N), fun(Acc, C) -> [C | Acc] end) + end. + +-file("src/gleam/list.gleam", 2051). +-spec combination_pairs_loop(list(ARO), list({ARO, ARO})) -> list({ARO, ARO}). +combination_pairs_loop(Items, Acc) -> + case Items of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + First_combinations = map(Rest, fun(Other) -> {First, Other} end), + Acc@1 = lists:reverse(First_combinations, Acc), + combination_pairs_loop(Rest, Acc@1) + end. + +-file("src/gleam/list.gleam", 2047). +?DOC( + " Return unique pair combinations of elements in the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert combination_pairs([1, 2, 3]) == [#(1, 2), #(1, 3), #(2, 3)]\n" + " ```\n" +). +-spec combination_pairs(list(ARL)) -> list({ARL, ARL}). +combination_pairs(Items) -> + combination_pairs_loop(Items, []). + +-file("src/gleam/list.gleam", 2107). +-spec take_firsts(list(list(ASI)), list(ASI), list(list(ASI))) -> {list(ASI), + list(list(ASI))}. +take_firsts(Rows, Column, Remaining_rows) -> + case Rows of + [] -> + {lists:reverse(Column), lists:reverse(Remaining_rows)}; + + [[] | Rest] -> + take_firsts(Rest, Column, Remaining_rows); + + [[First | Remaining_row] | Rest_rows] -> + Remaining_rows@1 = [Remaining_row | Remaining_rows], + take_firsts(Rest_rows, [First | Column], Remaining_rows@1) + end. + +-file("src/gleam/list.gleam", 2094). +-spec transpose_loop(list(list(ASB)), list(list(ASB))) -> list(list(ASB)). +transpose_loop(Rows, Columns) -> + case Rows of + [] -> + lists:reverse(Columns); + + _ -> + {Column, Rest} = take_firsts(Rows, [], []), + case Column of + [_ | _] -> + transpose_loop(Rest, [Column | Columns]); + + [] -> + transpose_loop(Rest, Columns) + end + end. + +-file("src/gleam/list.gleam", 2090). +?DOC( + " Transpose rows and columns of the list of lists.\n" + "\n" + " Notice: This function is not tail recursive,\n" + " and thus may exceed stack size if called,\n" + " with large lists (on the JavaScript target).\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert transpose([[1, 2, 3], [101, 102, 103]])\n" + " == [[1, 101], [2, 102], [3, 103]]\n" + " ```\n" +). +-spec transpose(list(list(ARW))) -> list(list(ARW)). +transpose(List_of_lists) -> + transpose_loop(List_of_lists, []). + +-file("src/gleam/list.gleam", 2071). +?DOC( + " Make a list alternating the elements from the given lists\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert interleave([[1, 2], [101, 102], [201, 202]])\n" + " == [1, 101, 201, 2, 102, 202]\n" + " ```\n" +). +-spec interleave(list(list(ARS))) -> list(ARS). +interleave(List) -> + _pipe = List, + _pipe@1 = transpose(_pipe), + lists:append(_pipe@1). + +-file("src/gleam/list.gleam", 2140). +-spec shuffle_pair_unwrap_loop(list({float(), ASU}), list(ASU)) -> list(ASU). +shuffle_pair_unwrap_loop(List, Acc) -> + case List of + [] -> + Acc; + + [Elem_pair | Enumerable] -> + shuffle_pair_unwrap_loop( + Enumerable, + [erlang:element(2, Elem_pair) | Acc] + ) + end. + +-file("src/gleam/list.gleam", 2148). +-spec do_shuffle_by_pair_indexes(list({float(), ASY})) -> list({float(), ASY}). +do_shuffle_by_pair_indexes(List_of_pairs) -> + sort( + List_of_pairs, + fun(A_pair, B_pair) -> + gleam@float:compare( + erlang:element(1, A_pair), + erlang:element(1, B_pair) + ) + end + ). + +-file("src/gleam/list.gleam", 2133). +?DOC( + " Takes a list, randomly sorts all items and returns the shuffled list.\n" + "\n" + " This function uses `float.random` to decide the order of the elements.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |> shuffle\n" + " // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5]\n" + " ```\n" +). +-spec shuffle(list(ASR)) -> list(ASR). +shuffle(List) -> + _pipe = List, + _pipe@1 = fold(_pipe, [], fun(Acc, A) -> [{rand:uniform(), A} | Acc] end), + _pipe@2 = do_shuffle_by_pair_indexes(_pipe@1), + shuffle_pair_unwrap_loop(_pipe@2, []). + +-file("src/gleam/list.gleam", 2178). +-spec max_loop(list(ATI), fun((ATI, ATI) -> gleam@order:order()), ATI) -> ATI. +max_loop(List, Compare, Max) -> + case List of + [] -> + Max; + + [First | Rest] -> + case Compare(First, Max) of + gt -> + max_loop(Rest, Compare, First); + + lt -> + max_loop(Rest, Compare, Max); + + eq -> + max_loop(Rest, Compare, Max) + end + end. + +-file("src/gleam/list.gleam", 2168). +?DOC( + " Takes a list and a comparator, and returns the maximum element in the list\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert [1, 2, 3, 4, 5] |> list.max(int.compare) == Ok(5)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert [\"a\", \"c\", \"b\"] |> list.max(string.compare) == Ok(\"c\")\n" + " ```\n" +). +-spec max(list(ATB), fun((ATB, ATB) -> gleam@order:order())) -> {ok, ATB} | + {error, nil}. +max(List, Compare) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + {ok, max_loop(Rest, Compare, First)} + end. + +-file("src/gleam/list.gleam", 2243). +-spec log_random() -> float(). +log_random() -> + Random@1 = case gleam@float:logarithm( + rand:uniform() + 2.2250738585072014e-308 + ) of + {ok, Random} -> Random; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam/list"/utf8>>, + function => <<"log_random"/utf8>>, + line => 2244, + value => _assert_fail, + start => 55129, + 'end' => 55200, + pattern_start => 55140, + pattern_end => 55150}) + end, + Random@1. + +-file("src/gleam/list.gleam", 2220). +-spec sample_loop( + list(ATM), + gleam@dict:dict(integer(), ATM), + integer(), + float() +) -> gleam@dict:dict(integer(), ATM). +sample_loop(List, Reservoir, N, W) -> + Skip = begin + Log@1 = case gleam@float:logarithm(1.0 - W) of + {ok, Log} -> Log; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam/list"/utf8>>, + function => <<"sample_loop"/utf8>>, + line => 2227, + value => _assert_fail, + start => 54690, + 'end' => 54736, + pattern_start => 54701, + pattern_end => 54708}) + end, + erlang:round(math:floor(case Log@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> log_random() / Gleam@denominator + end)) + end, + case drop(List, Skip) of + [] -> + Reservoir; + + [First | Rest] -> + Reservoir@1 = gleam@dict:insert( + Reservoir, + gleam@int:random(N), + First + ), + W@1 = W * math:exp(case erlang:float(N) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> log_random() / Gleam@denominator@1 + end), + sample_loop(Rest, Reservoir@1, N, W@1) + end. + +-file("src/gleam/list.gleam", 2259). +-spec build_reservoir_loop( + list(ATX), + integer(), + gleam@dict:dict(integer(), ATX) +) -> {gleam@dict:dict(integer(), ATX), list(ATX)}. +build_reservoir_loop(List, Size, Reservoir) -> + Reservoir_size = maps:size(Reservoir), + case Reservoir_size >= Size of + true -> + {Reservoir, List}; + + false -> + case List of + [] -> + {Reservoir, []}; + + [First | Rest] -> + Reservoir@1 = gleam@dict:insert( + Reservoir, + Reservoir_size, + First + ), + build_reservoir_loop(Rest, Size, Reservoir@1) + end + end. + +-file("src/gleam/list.gleam", 2255). +?DOC( + " Builds the initial reservoir used by Algorithm L.\n" + " This is a dictionary with keys ranging from `0` up to `n - 1` where each\n" + " value is the corresponding element at that position in `list`.\n" + "\n" + " This also returns the remaining elements of `list` that didn't end up in\n" + " the reservoir.\n" +). +-spec build_reservoir(list(ATS), integer()) -> {gleam@dict:dict(integer(), ATS), + list(ATS)}. +build_reservoir(List, N) -> + build_reservoir_loop(List, N, maps:new()). + +-file("src/gleam/list.gleam", 2202). +?DOC( + " Returns a random sample of up to n elements from a list using reservoir\n" + " sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L).\n" + " Returns an empty list if the sample size is less than or equal to 0.\n" + "\n" + " Order is not random, only selection is.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " sample([1, 2, 3, 4, 5], 3)\n" + " // -> [2, 4, 5] // A random sample of 3 items\n" + " ```\n" +). +-spec sample(list(ATJ), integer()) -> list(ATJ). +sample(List, N) -> + {Reservoir, Rest} = build_reservoir(List, N), + case gleam@dict:is_empty(Reservoir) of + true -> + []; + + false -> + W = math:exp(case erlang:float(N) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> log_random() / Gleam@denominator + end), + maps:values(sample_loop(Rest, Reservoir, N, W)) + end. diff --git a/ui/javascript/gleam_stdlib/gleam@option.erl b/ui/javascript/gleam_stdlib/gleam@option.erl new file mode 100644 index 0000000..1204ae6 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@option.erl @@ -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, []). diff --git a/ui/javascript/gleam_stdlib/gleam@order.erl b/ui/javascript/gleam_stdlib/gleam@order.erl new file mode 100644 index 0000000..c4e87be --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@order.erl @@ -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. diff --git a/ui/javascript/gleam_stdlib/gleam@pair.erl b/ui/javascript/gleam_stdlib/gleam@pair.erl new file mode 100644 index 0000000..68ac227 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@pair.erl @@ -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}. diff --git a/ui/javascript/gleam_stdlib/gleam@result.erl b/ui/javascript/gleam_stdlib/gleam@result.erl new file mode 100644 index 0000000..7aed253 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@result.erl @@ -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. diff --git a/ui/javascript/gleam_stdlib/gleam@set.erl b/ui/javascript/gleam_stdlib/gleam@set.erl new file mode 100644 index 0000000..f461fe9 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@set.erl @@ -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 + ). diff --git a/ui/javascript/gleam_stdlib/gleam@string.erl b/ui/javascript/gleam_stdlib/gleam@string.erl new file mode 100644 index 0000000..7d3d1a1 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@string.erl @@ -0,0 +1,1006 @@ +-module(gleam@string). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/string.gleam"). +-export([is_empty/1, length/1, reverse/1, replace/3, lowercase/1, uppercase/1, compare/2, slice/3, crop/2, byte_size/1, drop_start/2, drop_end/2, contains/2, starts_with/2, ends_with/2, pop_grapheme/1, to_graphemes/1, split/2, split_once/2, append/2, concat/1, repeat/2, join/2, pad_start/3, pad_end/3, trim_end/1, trim_start/1, trim/1, to_utf_codepoints/1, from_utf_codepoints/1, utf_codepoint/1, utf_codepoint_to_int/1, to_option/1, first/1, last/1, capitalise/1, inspect/1, remove_prefix/2, remove_suffix/2]). +-export_type([direction/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( + " Strings in Gleam are UTF-8 binaries. They can be written in your code as\n" + " text surrounded by `\"double quotes\"`.\n" +). + +-type direction() :: leading | trailing. + +-file("src/gleam/string.gleam", 21). +?DOC( + " Determines if a `String` is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert is_empty(\"\")\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !is_empty(\"the world\")\n" + " ```\n" +). +-spec is_empty(binary()) -> boolean(). +is_empty(Str) -> + Str =:= <<""/utf8>>. + +-file("src/gleam/string.gleam", 46). +?DOC( + " Gets the number of grapheme clusters in a given `String`.\n" + "\n" + " This function has to iterate across the whole string to count the number of\n" + " graphemes, so it runs in linear time. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert length(\"Gleam\") == 5\n" + " ```\n" + "\n" + " ```gleam\n" + " assert length(\"ß↑e̊\") == 3\n" + " ```\n" + "\n" + " ```gleam\n" + " assert length(\"\") == 0\n" + " ```\n" +). +-spec length(binary()) -> integer(). +length(String) -> + string:length(String). + +-file("src/gleam/string.gleam", 59). +?DOC( + " Reverses a `String`.\n" + "\n" + " This function has to iterate across the whole `String` so it runs in linear\n" + " time. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert reverse(\"stressed\") == \"desserts\"\n" + " ```\n" +). +-spec reverse(binary()) -> binary(). +reverse(String) -> + _pipe = String, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = string:reverse(_pipe@1), + unicode:characters_to_binary(_pipe@2). + +-file("src/gleam/string.gleam", 78). +?DOC( + " Creates a new `String` by replacing all occurrences of a given substring.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert replace(\"www.example.com\", each: \".\", with: \"-\") == \"www-example-com\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert replace(\"a,b,c,d,e\", each: \",\", with: \"/\") == \"a/b/c/d/e\"\n" + " ```\n" +). +-spec replace(binary(), binary(), binary()) -> binary(). +replace(String, Pattern, Substitute) -> + _pipe = String, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = gleam_stdlib:string_replace(_pipe@1, Pattern, Substitute), + unicode:characters_to_binary(_pipe@2). + +-file("src/gleam/string.gleam", 102). +?DOC( + " Creates a new `String` with all the graphemes in the input `String` converted to\n" + " lowercase.\n" + "\n" + " Useful for case-insensitive comparisons.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert lowercase(\"X-FILES\") == \"x-files\"\n" + " ```\n" +). +-spec lowercase(binary()) -> binary(). +lowercase(String) -> + string:lowercase(String). + +-file("src/gleam/string.gleam", 117). +?DOC( + " Creates a new `String` with all the graphemes in the input `String` converted to\n" + " uppercase.\n" + "\n" + " Useful for case-insensitive comparisons and VIRTUAL YELLING.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert uppercase(\"skinner\") == \"SKINNER\"\n" + " ```\n" +). +-spec uppercase(binary()) -> binary(). +uppercase(String) -> + string:uppercase(String). + +-file("src/gleam/string.gleam", 137). +?DOC( + " Compares two `String`s to see which is \"larger\" by comparing their graphemes.\n" + "\n" + " This does not compare the size or length of the given `String`s.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/order\n" + "\n" + " assert compare(\"Anthony\", \"Anthony\") == order.Eq\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/order\n" + "\n" + " assert compare(\"A\", \"B\") == order.Lt\n" + " ```\n" +). +-spec compare(binary(), binary()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + _ -> + case gleam_stdlib:less_than(A, B) of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/string.gleam", 181). +?DOC( + " Takes a substring given a start grapheme index and a length. Negative indexes\n" + " are taken starting from the *end* of the string.\n" + "\n" + " This function runs in linear time with the size of the index and the\n" + " length. Negative indexes are linear with the size of the input string in\n" + " addition to the other costs.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert slice(from: \"gleam\", at_index: 1, length: 2) == \"le\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert slice(from: \"gleam\", at_index: 1, length: 10) == \"leam\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert slice(from: \"gleam\", at_index: 10, length: 3) == \"\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert slice(from: \"gleam\", at_index: -2, length: 2) == \"am\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert slice(from: \"gleam\", at_index: -12, length: 2) == \"\"\n" + " ```\n" +). +-spec slice(binary(), integer(), integer()) -> binary(). +slice(String, Idx, Len) -> + case Len =< 0 of + true -> + <<""/utf8>>; + + false -> + case Idx < 0 of + true -> + Translated_idx = string:length(String) + Idx, + case Translated_idx < 0 of + true -> + <<""/utf8>>; + + false -> + gleam_stdlib:slice(String, Translated_idx, Len) + end; + + false -> + gleam_stdlib:slice(String, Idx, Len) + end + end. + +-file("src/gleam/string.gleam", 218). +?DOC( + " Drops contents of the first `String` that occur before the second `String`.\n" + " If the `from` string does not contain the `before` string, `from` is\n" + " returned unchanged.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert crop(from: \"The Lone Gunmen\", before: \"Lone\") == \"Lone Gunmen\"\n" + " ```\n" +). +-spec crop(binary(), binary()) -> binary(). +crop(String, Substring) -> + gleam_stdlib:crop_string(String, Substring). + +-file("src/gleam/string.gleam", 852). +?DOC( + " Returns the number of bytes in a `String`.\n" + "\n" + " This function runs in constant time on Erlang and in linear time on\n" + " JavaScript.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert byte_size(\"🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻\") == 58\n" + " ```\n" +). +-spec byte_size(binary()) -> integer(). +byte_size(String) -> + erlang:byte_size(String). + +-file("src/gleam/string.gleam", 230). +?DOC( + " Drops *n* graphemes from the start of a `String`.\n" + "\n" + " This function runs in linear time with the number of graphemes to drop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert drop_start(from: \"The Lone Gunmen\", up_to: 2) == \"e Lone Gunmen\"\n" + " ```\n" +). +-spec drop_start(binary(), integer()) -> binary(). +drop_start(String, Num_graphemes) -> + case Num_graphemes =< 0 of + true -> + String; + + false -> + Prefix = gleam_stdlib:slice(String, 0, Num_graphemes), + Prefix_size = erlang:byte_size(Prefix), + binary:part( + String, + Prefix_size, + erlang:byte_size(String) - Prefix_size + ) + end. + +-file("src/gleam/string.gleam", 253). +?DOC( + " Drops *n* graphemes from the end of a `String`.\n" + "\n" + " This function traverses the full string, so it runs in linear time with the\n" + " size of the string. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert drop_end(from: \"Cigarette Smoking Man\", up_to: 2)\n" + " == \"Cigarette Smoking M\"\n" + " ```\n" +). +-spec drop_end(binary(), integer()) -> binary(). +drop_end(String, Num_graphemes) -> + case Num_graphemes =< 0 of + true -> + String; + + false -> + slice(String, 0, string:length(String) - Num_graphemes) + end. + +-file("src/gleam/string.gleam", 278). +?DOC( + " Checks if the first `String` contains the second.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert contains(does: \"theory\", contain: \"ory\")\n" + " ```\n" + "\n" + " ```gleam\n" + " assert contains(does: \"theory\", contain: \"the\")\n" + " ```\n" + "\n" + " ```gleam\n" + " assert !contains(does: \"theory\", contain: \"THE\")\n" + " ```\n" +). +-spec contains(binary(), binary()) -> boolean(). +contains(Haystack, Needle) -> + gleam_stdlib:contains_string(Haystack, Needle). + +-file("src/gleam/string.gleam", 290). +?DOC( + " Checks whether the first `String` starts with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert !starts_with(\"theory\", \"ory\")\n" + " ```\n" +). +-spec starts_with(binary(), binary()) -> boolean(). +starts_with(String, Prefix) -> + gleam_stdlib:string_starts_with(String, Prefix). + +-file("src/gleam/string.gleam", 302). +?DOC( + " Checks whether the first `String` ends with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert ends_with(\"theory\", \"ory\")\n" + " ```\n" +). +-spec ends_with(binary(), binary()) -> boolean(). +ends_with(String, Suffix) -> + gleam_stdlib:string_ends_with(String, Suffix). + +-file("src/gleam/string.gleam", 594). +?DOC( + " Splits a non-empty `String` into its first element (head) and rest (tail).\n" + " This lets you pattern match on `String`s exactly as you would with lists.\n" + "\n" + " ## Performance\n" + "\n" + " There is a notable overhead to using this function, so you may not want to\n" + " use it in a tight loop. If you wish to efficiently parse a string you may\n" + " want to use alternatives such as the [splitter package](https://hex.pm/packages/splitter).\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert pop_grapheme(\"gleam\") == Ok(#(\"g\", \"leam\"))\n" + " ```\n" + "\n" + " ```gleam\n" + " assert pop_grapheme(\"\") == Error(Nil)\n" + " ```\n" +). +-spec pop_grapheme(binary()) -> {ok, {binary(), binary()}} | {error, nil}. +pop_grapheme(String) -> + gleam_stdlib:string_pop_grapheme(String). + +-file("src/gleam/string.gleam", 610). +-spec to_graphemes_loop(binary(), list(binary())) -> list(binary()). +to_graphemes_loop(String, Acc) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {Grapheme, Rest}} -> + to_graphemes_loop(Rest, [Grapheme | Acc]); + + {error, _} -> + Acc + end. + +-file("src/gleam/string.gleam", 604). +?DOC( + " Converts a `String` to a list of\n" + " [graphemes](https://en.wikipedia.org/wiki/Grapheme).\n" + "\n" + " ```gleam\n" + " assert to_graphemes(\"abc\") == [\"a\", \"b\", \"c\"]\n" + " ```\n" +). +-spec to_graphemes(binary()) -> list(binary()). +to_graphemes(String) -> + _pipe = String, + _pipe@1 = to_graphemes_loop(_pipe, []), + lists:reverse(_pipe@1). + +-file("src/gleam/string.gleam", 313). +?DOC( + " Creates a list of `String`s by splitting a given string on a given substring.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert split(\"home/gleam/desktop/\", on: \"/\")\n" + " == [\"home\", \"gleam\", \"desktop\", \"\"]\n" + " ```\n" +). +-spec split(binary(), binary()) -> list(binary()). +split(X, Substring) -> + case Substring of + <<""/utf8>> -> + to_graphemes(X); + + _ -> + _pipe = X, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = gleam@string_tree:split(_pipe@1, Substring), + gleam@list:map(_pipe@2, fun unicode:characters_to_binary/1) + end. + +-file("src/gleam/string.gleam", 340). +?DOC( + " Splits a `String` a single time on the given substring.\n" + "\n" + " Returns an `Error` if substring not present.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert split_once(\"home/gleam/desktop/\", on: \"/\")\n" + " == Ok(#(\"home\", \"gleam/desktop/\"))\n" + " ```\n" + "\n" + " ```gleam\n" + " assert split_once(\"home/gleam/desktop/\", on: \"?\") == Error(Nil)\n" + " ```\n" +). +-spec split_once(binary(), binary()) -> {ok, {binary(), binary()}} | + {error, nil}. +split_once(String, Substring) -> + case string:split(String, Substring) of + [First, Rest] -> + {ok, {First, Rest}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/string.gleam", 370). +?DOC( + " Creates a new `String` by joining two `String`s together.\n" + "\n" + " This function typically copies both `String`s and runs in linear time, but\n" + " the exact behaviour will depend on how the runtime you are using optimises\n" + " your code. Benchmark and profile your code if you need to understand its\n" + " performance better.\n" + "\n" + " If you are joining together large string and want to avoid copying any data\n" + " you may want to investigate using the [`string_tree`](../gleam/string_tree.html)\n" + " module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert append(to: \"butter\", suffix: \"fly\") == \"butterfly\"\n" + " ```\n" +). +-spec append(binary(), binary()) -> binary(). +append(First, Second) -> + <>. + +-file("src/gleam/string.gleam", 389). +-spec concat_loop(list(binary()), binary()) -> binary(). +concat_loop(Strings, Accumulator) -> + case Strings of + [String | Strings@1] -> + concat_loop(Strings@1, <>); + + [] -> + Accumulator + end. + +-file("src/gleam/string.gleam", 385). +?DOC( + " Creates a new `String` by joining many `String`s together.\n" + "\n" + " This function copies all the `String`s and runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert concat([\"never\", \"the\", \"less\"]) == \"nevertheless\"\n" + " ```\n" +). +-spec concat(list(binary())) -> binary(). +concat(Strings) -> + erlang:list_to_binary(Strings). + +-file("src/gleam/string.gleam", 413). +-spec repeat_loop(integer(), binary(), binary()) -> binary(). +repeat_loop(Times, Doubling_acc, Acc) -> + Acc@1 = case Times rem 2 of + 0 -> + Acc; + + _ -> + <> + end, + Times@1 = Times div 2, + case Times@1 =< 0 of + true -> + Acc@1; + + false -> + repeat_loop( + Times@1, + <>, + Acc@1 + ) + end. + +-file("src/gleam/string.gleam", 406). +?DOC( + " Creates a new `String` by repeating a `String` a given number of times.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert repeat(\"ha\", times: 3) == \"hahaha\"\n" + " ```\n" +). +-spec repeat(binary(), integer()) -> binary(). +repeat(String, Times) -> + case Times =< 0 of + true -> + <<""/utf8>>; + + false -> + repeat_loop(Times, String, <<""/utf8>>) + end. + +-file("src/gleam/string.gleam", 442). +-spec join_loop(list(binary()), binary(), binary()) -> binary(). +join_loop(Strings, Separator, Accumulator) -> + case Strings of + [] -> + Accumulator; + + [String | Strings@1] -> + join_loop( + Strings@1, + Separator, + <<<>/binary, + String/binary>> + ) + end. + +-file("src/gleam/string.gleam", 435). +?DOC( + " Joins many `String`s together with a given separator.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert join([\"home\",\"evan\",\"Desktop\"], with: \"/\") == \"home/evan/Desktop\"\n" + " ```\n" +). +-spec join(list(binary()), binary()) -> binary(). +join(Strings, Separator) -> + case Strings of + [] -> + <<""/utf8>>; + + [First | Rest] -> + join_loop(Rest, Separator, First) + end. + +-file("src/gleam/string.gleam", 514). +-spec padding(integer(), binary()) -> binary(). +padding(Size, Pad_string) -> + Pad_string_length = string:length(Pad_string), + Num_pads = case Pad_string_length of + 0 -> 0; + Gleam@denominator -> Size div Gleam@denominator + end, + Extra = case Pad_string_length of + 0 -> 0; + Gleam@denominator@1 -> Size rem Gleam@denominator@1 + end, + <<(repeat(Pad_string, Num_pads))/binary, + (slice(Pad_string, 0, Extra))/binary>>. + +-file("src/gleam/string.gleam", 470). +?DOC( + " Pads the start of a `String` until it has a given length.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert pad_start(\"121\", to: 5, with: \".\") == \"..121\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert pad_start(\"121\", to: 3, with: \".\") == \"121\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert pad_start(\"121\", to: 2, with: \".\") == \"121\"\n" + " ```\n" +). +-spec pad_start(binary(), integer(), binary()) -> binary(). +pad_start(String, Desired_length, Pad_string) -> + Current_length = string:length(String), + To_pad_length = Desired_length - Current_length, + case To_pad_length =< 0 of + true -> + String; + + false -> + <<(padding(To_pad_length, Pad_string))/binary, String/binary>> + end. + +-file("src/gleam/string.gleam", 500). +?DOC( + " Pads the end of a `String` until it has a given length.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert pad_end(\"123\", to: 5, with: \".\") == \"123..\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert pad_end(\"123\", to: 3, with: \".\") == \"123\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert pad_end(\"123\", to: 2, with: \".\") == \"123\"\n" + " ```\n" +). +-spec pad_end(binary(), integer(), binary()) -> binary(). +pad_end(String, Desired_length, Pad_string) -> + Current_length = string:length(String), + To_pad_length = Desired_length - Current_length, + case To_pad_length =< 0 of + true -> + String; + + false -> + <> + end. + +-file("src/gleam/string.gleam", 569). +?DOC( + " Removes whitespace at the end of a `String`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert trim_end(\" hats \\n\") == \" hats\"\n" + " ```\n" +). +-spec trim_end(binary()) -> binary(). +trim_end(String) -> + string:trim(String, trailing). + +-file("src/gleam/string.gleam", 556). +?DOC( + " Removes whitespace at the start of a `String`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert trim_start(\" hats \\n\") == \"hats \\n\"\n" + " ```\n" +). +-spec trim_start(binary()) -> binary(). +trim_start(String) -> + string:trim(String, leading). + +-file("src/gleam/string.gleam", 535). +?DOC( + " Removes whitespace on both sides of a `String`.\n" + "\n" + " Whitespace in this function is the set of nonbreakable whitespace\n" + " codepoints, defined as Pattern_White_Space in [Unicode Standard Annex #31][1].\n" + "\n" + " [1]: https://unicode.org/reports/tr31/\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert trim(\" hats \\n\") == \"hats\"\n" + " ```\n" +). +-spec trim(binary()) -> binary(). +trim(String) -> + _pipe = String, + _pipe@1 = trim_start(_pipe), + trim_end(_pipe@1). + +-file("src/gleam/string.gleam", 656). +-spec to_utf_codepoints_loop(bitstring(), list(integer())) -> list(integer()). +to_utf_codepoints_loop(Bit_array, Acc) -> + case Bit_array of + <> -> + to_utf_codepoints_loop(Rest, [First | Acc]); + + _ -> + lists:reverse(Acc) + end. + +-file("src/gleam/string.gleam", 651). +-spec do_to_utf_codepoints(binary()) -> list(integer()). +do_to_utf_codepoints(String) -> + to_utf_codepoints_loop(<>, []). + +-file("src/gleam/string.gleam", 646). +?DOC( + " Converts a `String` to a `List` of `UtfCodepoint`.\n" + "\n" + " See and\n" + " for an\n" + " explanation on code points.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert \"a\" |> to_utf_codepoints == [UtfCodepoint(97)]\n" + " ```\n" + "\n" + " ```gleam\n" + " // Semantically the same as:\n" + " // [\"🏳\", \"️\", \"‍\", \"🌈\"] or:\n" + " // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow]\n" + " assert \"🏳️‍🌈\" |> to_utf_codepoints\n" + " == [\n" + " UtfCodepoint(127987),\n" + " UtfCodepoint(65039),\n" + " UtfCodepoint(8205),\n" + " UtfCodepoint(127752),\n" + " ]\n" + " ```\n" +). +-spec to_utf_codepoints(binary()) -> list(integer()). +to_utf_codepoints(String) -> + do_to_utf_codepoints(String). + +-file("src/gleam/string.gleam", 695). +?DOC( + " Converts a `List` of `UtfCodepoint`s to a `String`.\n" + "\n" + " See and\n" + " for an\n" + " explanation on code points.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(a) = utf_codepoint(97)\n" + " let assert Ok(b) = utf_codepoint(98)\n" + " let assert Ok(c) = utf_codepoint(99)\n" + " assert from_utf_codepoints([a, b, c]) == \"abc\"\n" + " ```\n" +). +-spec from_utf_codepoints(list(integer())) -> binary(). +from_utf_codepoints(Utf_codepoints) -> + gleam_stdlib:utf_codepoint_list_to_string(Utf_codepoints). + +-file("src/gleam/string.gleam", 701). +?DOC( + " Converts an integer to a `UtfCodepoint`.\n" + "\n" + " Returns an `Error` if the integer does not represent a valid UTF codepoint.\n" +). +-spec utf_codepoint(integer()) -> {ok, integer()} | {error, nil}. +utf_codepoint(Value) -> + case Value of + I when I > 1114111 -> + {error, nil}; + + I@1 when (I@1 >= 55296) andalso (I@1 =< 57343) -> + {error, nil}; + + I@2 when I@2 < 0 -> + {error, nil}; + + I@3 -> + {ok, gleam_stdlib:identity(I@3)} + end. + +-file("src/gleam/string.gleam", 721). +?DOC( + " Converts a `UtfCodepoint` to its ordinal code point value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert [utf_codepoint, ..] = to_utf_codepoints(\"💜\")\n" + " assert utf_codepoint_to_int(utf_codepoint) == 128156\n" + " ```\n" +). +-spec utf_codepoint_to_int(integer()) -> integer(). +utf_codepoint_to_int(Cp) -> + gleam_stdlib:identity(Cp). + +-file("src/gleam/string.gleam", 736). +?DOC( + " Converts a `String` into `Option(String)` where an empty `String` becomes\n" + " `None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert to_option(\"\") == None\n" + " ```\n" + "\n" + " ```gleam\n" + " assert to_option(\"hats\") == Some(\"hats\")\n" + " ```\n" +). +-spec to_option(binary()) -> gleam@option:option(binary()). +to_option(String) -> + case String of + <<""/utf8>> -> + none; + + _ -> + {some, String} + end. + +-file("src/gleam/string.gleam", 757). +?DOC( + " Returns the first grapheme cluster in a given `String` and wraps it in a\n" + " `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.\n" + " Otherwise, it returns `Ok(String)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert first(\"\") == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert first(\"icecream\") == Ok(\"i\")\n" + " ```\n" +). +-spec first(binary()) -> {ok, binary()} | {error, nil}. +first(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, _}} -> + {ok, First}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/string.gleam", 781). +?DOC( + " Returns the last grapheme cluster in a given `String` and wraps it in a\n" + " `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.\n" + " Otherwise, it returns `Ok(String)`.\n" + "\n" + " This function traverses the full string, so it runs in linear time with the\n" + " length of the string. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert last(\"\") == Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " assert last(\"icecream\") == Ok(\"m\")\n" + " ```\n" +). +-spec last(binary()) -> {ok, binary()} | {error, nil}. +last(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, <<""/utf8>>}} -> + {ok, First}; + + {ok, {_, Rest}} -> + {ok, slice(Rest, -1, 1)}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/string.gleam", 798). +?DOC( + " Creates a new `String` with the first grapheme in the input `String`\n" + " converted to uppercase and the remaining graphemes to lowercase.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert capitalise(\"mamouna\") == \"Mamouna\"\n" + " ```\n" +). +-spec capitalise(binary()) -> binary(). +capitalise(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, Rest}} -> + append(string:uppercase(First), string:lowercase(Rest)); + + {error, _} -> + <<""/utf8>> + end. + +-file("src/gleam/string.gleam", 829). +?DOC( + " Returns a `String` representation of a term in Gleam syntax.\n" + "\n" + " This may be occasionally useful for quick-and-dirty printing of values in\n" + " scripts. For error reporting and other uses prefer constructing strings by\n" + " pattern matching on the values.\n" + "\n" + " ## Limitations\n" + "\n" + " The output format of this function is not stable and could change at any\n" + " time. The output is not suitable for parsing.\n" + "\n" + " This function works using runtime reflection, so the output may not be\n" + " perfectly accurate for data structures where the runtime structure doesn't\n" + " hold enough information to determine the original syntax. For example,\n" + " tuples with an Erlang atom in the first position will be mistaken for Gleam\n" + " records.\n" + "\n" + " ## Security and safety\n" + "\n" + " There is no limit to how large the strings that this function can produce.\n" + " Be careful not to call this function with large data structures or you\n" + " could use very large amounts of memory, potentially causing runtime\n" + " problems.\n" +). +-spec inspect(any()) -> binary(). +inspect(Term) -> + _pipe = Term, + _pipe@1 = gleam_stdlib:inspect(_pipe), + unicode:characters_to_binary(_pipe@1). + +-file("src/gleam/string.gleam", 871). +?DOC( + " Removes the given prefix from the start of a `String`, if present.\n" + "\n" + " If the `String` does not start with the given prefix the string is returned\n" + " unchanged.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert remove_prefix(\"@lpil\", \"@\") == \"lpil\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert remove_prefix(\"hello!\", \"@\") == \"hello!\"\n" + " ```\n" +). +-spec remove_prefix(binary(), binary()) -> binary(). +remove_prefix(String, Prefix) -> + gleam_stdlib:string_remove_prefix(String, Prefix). + +-file("src/gleam/string.gleam", 890). +?DOC( + " Removes the given suffix from the end of a `String`, if present.\n" + "\n" + " If the `String` does not end with the given suffix the string is returned\n" + " unchanged.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert remove_suffix(\"Hello!\", \"!\") == \"Hello\"\n" + " ```\n" + "\n" + " ```gleam\n" + " assert remove_suffix(\"Hello!?\", \"!\") == \"Hello!?\"\n" + " ```\n" +). +-spec remove_suffix(binary(), binary()) -> binary(). +remove_suffix(String, Suffix) -> + gleam_stdlib:string_remove_suffix(String, Suffix). diff --git a/ui/javascript/gleam_stdlib/gleam@string_tree.erl b/ui/javascript/gleam_stdlib/gleam@string_tree.erl new file mode 100644 index 0000000..2b09b6c --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@string_tree.erl @@ -0,0 +1,202 @@ +-module(gleam@string_tree). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/string_tree.gleam"). +-export([from_strings/1, new/0, from_string/1, append_tree/2, prepend/2, append/2, prepend_tree/2, concat/1, to_string/1, byte_size/1, join/2, lowercase/1, uppercase/1, reverse/1, split/2, replace/3, is_equal/2, is_empty/1]). +-export_type([string_tree/0, direction/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 string_tree() :: any(). + +-type direction() :: all. + +-file("src/gleam/string_tree.gleam", 69). +?DOC( + " Converts a list of strings into a `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_strings(list(binary())) -> string_tree(). +from_strings(Strings) -> + gleam_stdlib:identity(Strings). + +-file("src/gleam/string_tree.gleam", 24). +?DOC( + " Create an empty `StringTree`. Useful as the start of a pipe chaining many\n" + " trees together.\n" +). +-spec new() -> string_tree(). +new() -> + gleam_stdlib:identity([]). + +-file("src/gleam/string_tree.gleam", 85). +?DOC( + " Converts a string into a `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_string(binary()) -> string_tree(). +from_string(String) -> + gleam_stdlib:identity(String). + +-file("src/gleam/string_tree.gleam", 61). +?DOC( + " Appends some `StringTree` onto the end of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec append_tree(string_tree(), string_tree()) -> string_tree(). +append_tree(Tree, Suffix) -> + gleam_stdlib:iodata_append(Tree, Suffix). + +-file("src/gleam/string_tree.gleam", 32). +?DOC( + " Prepends a `String` onto the start of some `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend(string_tree(), binary()) -> string_tree(). +prepend(Tree, Prefix) -> + gleam_stdlib:iodata_append(gleam_stdlib:identity(Prefix), Tree). + +-file("src/gleam/string_tree.gleam", 40). +?DOC( + " Appends a `String` onto the end of some `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec append(string_tree(), binary()) -> string_tree(). +append(Tree, Second) -> + gleam_stdlib:iodata_append(Tree, gleam_stdlib:identity(Second)). + +-file("src/gleam/string_tree.gleam", 48). +?DOC( + " Prepends some `StringTree` onto the start of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend_tree(string_tree(), string_tree()) -> string_tree(). +prepend_tree(Tree, Prefix) -> + gleam_stdlib:iodata_append(Prefix, Tree). + +-file("src/gleam/string_tree.gleam", 77). +?DOC( + " Joins a list of trees into a single tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat(list(string_tree())) -> string_tree(). +concat(Trees) -> + gleam_stdlib:identity(Trees). + +-file("src/gleam/string_tree.gleam", 94). +?DOC( + " Turns a `StringTree` into a `String`.\n" + "\n" + " This function is implemented natively by the virtual machine and is highly\n" + " optimised.\n" +). +-spec to_string(string_tree()) -> binary(). +to_string(Tree) -> + unicode:characters_to_binary(Tree). + +-file("src/gleam/string_tree.gleam", 100). +?DOC(" Returns the size of the `StringTree` in bytes.\n"). +-spec byte_size(string_tree()) -> integer(). +byte_size(Tree) -> + erlang:iolist_size(Tree). + +-file("src/gleam/string_tree.gleam", 104). +?DOC(" Joins the given trees into a new tree separated with the given string.\n"). +-spec join(list(string_tree()), binary()) -> string_tree(). +join(Trees, Sep) -> + _pipe = Trees, + _pipe@1 = gleam@list:intersperse(_pipe, gleam_stdlib:identity(Sep)), + gleam_stdlib:identity(_pipe@1). + +-file("src/gleam/string_tree.gleam", 115). +?DOC( + " Converts a `StringTree` to a new one where the contents have been\n" + " lowercased.\n" +). +-spec lowercase(string_tree()) -> string_tree(). +lowercase(Tree) -> + string:lowercase(Tree). + +-file("src/gleam/string_tree.gleam", 122). +?DOC( + " Converts a `StringTree` to a new one where the contents have been\n" + " uppercased.\n" +). +-spec uppercase(string_tree()) -> string_tree(). +uppercase(Tree) -> + string:uppercase(Tree). + +-file("src/gleam/string_tree.gleam", 127). +?DOC(" Converts a `StringTree` to a new one with the contents reversed.\n"). +-spec reverse(string_tree()) -> string_tree(). +reverse(Tree) -> + string:reverse(Tree). + +-file("src/gleam/string_tree.gleam", 145). +?DOC(" Splits a `StringTree` on a given pattern into a list of trees.\n"). +-spec split(string_tree(), binary()) -> list(string_tree()). +split(Tree, Pattern) -> + string:split(Tree, Pattern, all). + +-file("src/gleam/string_tree.gleam", 156). +?DOC(" Replaces all instances of a pattern with a given string substitute.\n"). +-spec replace(string_tree(), binary(), binary()) -> string_tree(). +replace(Tree, Pattern, Substitute) -> + gleam_stdlib:string_replace(Tree, Pattern, Substitute). + +-file("src/gleam/string_tree.gleam", 180). +?DOC( + " Compares two string trees to determine if they have the same textual\n" + " content.\n" + "\n" + " Comparing two string trees using the `==` operator may return `False` even\n" + " if they have the same content as they may have been built in different ways,\n" + " so using this function is often preferred.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert from_strings([\"a\", \"b\"]) != from_string(\"ab\")\n" + " ```\n" + "\n" + " ```gleam\n" + " assert is_equal(from_strings([\"a\", \"b\"]), from_string(\"ab\"))\n" + " ```\n" +). +-spec is_equal(string_tree(), string_tree()) -> boolean(). +is_equal(A, B) -> + string:equal(A, B). + +-file("src/gleam/string_tree.gleam", 201). +?DOC( + " Inspects a `StringTree` to determine if it is equivalent to an empty string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert !{ from_string(\"ok\") |> is_empty }\n" + " ```\n" + "\n" + " ```gleam\n" + " assert from_string(\"\") |> is_empty\n" + " ```\n" + "\n" + " ```gleam\n" + " assert from_strings([]) |> is_empty\n" + " ```\n" +). +-spec is_empty(string_tree()) -> boolean(). +is_empty(Tree) -> + string:is_empty(Tree). diff --git a/ui/javascript/gleam_stdlib/gleam@uri.erl b/ui/javascript/gleam_stdlib/gleam@uri.erl new file mode 100644 index 0000000..2124ff7 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam@uri.erl @@ -0,0 +1,1042 @@ +-module(gleam@uri). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/uri.gleam"). +-export([parse/1, parse_query/1, percent_encode/1, query_to_string/1, percent_decode/1, path_segments/1, to_string/1, origin/1, merge/2]). +-export_type([uri/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( + " Utilities for working with URIs\n" + "\n" + " This module provides functions for working with URIs (for example, parsing\n" + " URIs or encoding query strings). The functions in this module are implemented\n" + " according to [RFC 3986](https://tools.ietf.org/html/rfc3986).\n" + "\n" + " Query encoding (Form encoding) is defined in the\n" + " [W3C specification](https://www.w3.org/TR/html52/sec-forms.html#urlencoded-form-data).\n" +). + +-type uri() :: {uri, + gleam@option:option(binary()), + gleam@option:option(binary()), + gleam@option:option(binary()), + gleam@option:option(integer()), + binary(), + gleam@option:option(binary()), + gleam@option:option(binary())}. + +-file("src/gleam/uri.gleam", 506). +-spec parse_fragment(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_fragment(Rest, Pieces) -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + {some, Rest}}}. + +-file("src/gleam/uri.gleam", 478). +-spec parse_query_with_question_mark_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_query_with_question_mark_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"#"/utf8, Rest/binary>> when Size =:= 0 -> + parse_fragment(Rest, Pieces); + + <<"#"/utf8, Rest@1/binary>> -> + Query = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + {some, Query}, + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@1); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + {some, Original}, + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_query_with_question_mark_loop( + Original, + Rest@2, + Pieces, + Size + 1 + ) + end. + +-file("src/gleam/uri.gleam", 471). +-spec parse_query_with_question_mark(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_query_with_question_mark(Uri_string, Pieces) -> + parse_query_with_question_mark_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 437). +-spec parse_path_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_path_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"?"/utf8, Rest/binary>> -> + Path = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Path, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest, Pieces@1); + + <<"#"/utf8, Rest@1/binary>> -> + Path@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Path@1, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@2); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Original, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_path_loop(Original, Rest@2, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 433). +-spec parse_path(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_path(Uri_string, Pieces) -> + parse_path_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 388). +-spec parse_port_loop(binary(), uri(), integer()) -> {ok, uri()} | {error, nil}. +parse_port_loop(Uri_string, Pieces, Port) -> + case Uri_string of + <<"0"/utf8, Rest/binary>> -> + parse_port_loop(Rest, Pieces, Port * 10); + + <<"1"/utf8, Rest@1/binary>> -> + parse_port_loop(Rest@1, Pieces, (Port * 10) + 1); + + <<"2"/utf8, Rest@2/binary>> -> + parse_port_loop(Rest@2, Pieces, (Port * 10) + 2); + + <<"3"/utf8, Rest@3/binary>> -> + parse_port_loop(Rest@3, Pieces, (Port * 10) + 3); + + <<"4"/utf8, Rest@4/binary>> -> + parse_port_loop(Rest@4, Pieces, (Port * 10) + 4); + + <<"5"/utf8, Rest@5/binary>> -> + parse_port_loop(Rest@5, Pieces, (Port * 10) + 5); + + <<"6"/utf8, Rest@6/binary>> -> + parse_port_loop(Rest@6, Pieces, (Port * 10) + 6); + + <<"7"/utf8, Rest@7/binary>> -> + parse_port_loop(Rest@7, Pieces, (Port * 10) + 7); + + <<"8"/utf8, Rest@8/binary>> -> + parse_port_loop(Rest@8, Pieces, (Port * 10) + 8); + + <<"9"/utf8, Rest@9/binary>> -> + parse_port_loop(Rest@9, Pieces, (Port * 10) + 9); + + <<"?"/utf8, Rest@10/binary>> -> + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@10, Pieces@1); + + <<"#"/utf8, Rest@11/binary>> -> + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@11, Pieces@2); + + <<"/"/utf8, _/binary>> -> + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@3); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 353). +-spec parse_port(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_port(Uri_string, Pieces) -> + case Uri_string of + <<":0"/utf8, Rest/binary>> -> + parse_port_loop(Rest, Pieces, 0); + + <<":1"/utf8, Rest@1/binary>> -> + parse_port_loop(Rest@1, Pieces, 1); + + <<":2"/utf8, Rest@2/binary>> -> + parse_port_loop(Rest@2, Pieces, 2); + + <<":3"/utf8, Rest@3/binary>> -> + parse_port_loop(Rest@3, Pieces, 3); + + <<":4"/utf8, Rest@4/binary>> -> + parse_port_loop(Rest@4, Pieces, 4); + + <<":5"/utf8, Rest@5/binary>> -> + parse_port_loop(Rest@5, Pieces, 5); + + <<":6"/utf8, Rest@6/binary>> -> + parse_port_loop(Rest@6, Pieces, 6); + + <<":7"/utf8, Rest@7/binary>> -> + parse_port_loop(Rest@7, Pieces, 7); + + <<":8"/utf8, Rest@8/binary>> -> + parse_port_loop(Rest@8, Pieces, 8); + + <<":9"/utf8, Rest@9/binary>> -> + parse_port_loop(Rest@9, Pieces, 9); + + <<":"/utf8>> -> + {ok, Pieces}; + + <<""/utf8>> -> + {ok, Pieces}; + + <<"?"/utf8, Rest@10/binary>> -> + parse_query_with_question_mark(Rest@10, Pieces); + + <<":?"/utf8, Rest@10/binary>> -> + parse_query_with_question_mark(Rest@10, Pieces); + + <<"#"/utf8, Rest@11/binary>> -> + parse_fragment(Rest@11, Pieces); + + <<":#"/utf8, Rest@11/binary>> -> + parse_fragment(Rest@11, Pieces); + + <<"/"/utf8, _/binary>> -> + parse_path(Uri_string, Pieces); + + <<":"/utf8, Rest@12/binary>> -> + case Rest@12 of + <<"/"/utf8, _/binary>> -> + parse_path(Rest@12, Pieces); + + _ -> + {error, nil} + end; + + _ -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 309). +-spec parse_host_outside_of_brackets_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_host_outside_of_brackets_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Original}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<":"/utf8, _/binary>> -> + Host = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Uri_string, Pieces@1); + + <<"/"/utf8, _/binary>> -> + Host@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@1}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@2); + + <<"?"/utf8, Rest/binary>> -> + Host@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@2}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest, Pieces@3); + + <<"#"/utf8, Rest@1/binary>> -> + Host@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@3}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@4); + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_host_outside_of_brackets_loop( + Original, + Rest@2, + Pieces, + Size + 1 + ) + end. + +-file("src/gleam/uri.gleam", 302). +-spec parse_host_outside_of_brackets(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_host_outside_of_brackets(Uri_string, Pieces) -> + parse_host_outside_of_brackets_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 289). +-spec is_valid_host_within_brackets_char(integer()) -> boolean(). +is_valid_host_within_brackets_char(Char) -> + (((((48 >= Char) andalso (Char =< 57)) orelse ((65 >= Char) andalso (Char =< 90))) + orelse ((97 >= Char) andalso (Char =< 122))) + orelse (Char =:= 58)) + orelse (Char =:= 46). + +-file("src/gleam/uri.gleam", 229). +-spec parse_host_within_brackets_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_host_within_brackets_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Uri_string}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<"]"/utf8, Rest/binary>> when Size =:= 0 -> + parse_port(Rest, Pieces); + + <<"]"/utf8, Rest@1/binary>> -> + Host = binary:part(Original, 0, Size + 1), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Rest@1, Pieces@1); + + <<"/"/utf8, _/binary>> when Size =:= 0 -> + parse_path(Uri_string, Pieces); + + <<"/"/utf8, _/binary>> -> + Host@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@1}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@2); + + <<"?"/utf8, Rest@2/binary>> when Size =:= 0 -> + parse_query_with_question_mark(Rest@2, Pieces); + + <<"?"/utf8, Rest@3/binary>> -> + Host@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@2}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@3, Pieces@3); + + <<"#"/utf8, Rest@4/binary>> when Size =:= 0 -> + parse_fragment(Rest@4, Pieces); + + <<"#"/utf8, Rest@5/binary>> -> + Host@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@3}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@5, Pieces@4); + + _ -> + {Char, Rest@6} = gleam_stdlib:string_pop_codeunit(Uri_string), + case is_valid_host_within_brackets_char(Char) of + true -> + parse_host_within_brackets_loop( + Original, + Rest@6, + Pieces, + Size + 1 + ); + + false -> + parse_host_outside_of_brackets_loop( + Original, + Original, + Pieces, + 0 + ) + end + end. + +-file("src/gleam/uri.gleam", 222). +-spec parse_host_within_brackets(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_host_within_brackets(Uri_string, Pieces) -> + parse_host_within_brackets_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 199). +-spec parse_host(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_host(Uri_string, Pieces) -> + case Uri_string of + <<"["/utf8, _/binary>> -> + parse_host_within_brackets(Uri_string, Pieces); + + <<":"/utf8, _/binary>> -> + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Uri_string, Pieces@1); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + parse_host_outside_of_brackets(Uri_string, Pieces) + end. + +-file("src/gleam/uri.gleam", 167). +-spec parse_userinfo_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_userinfo_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"@"/utf8, Rest/binary>> when Size =:= 0 -> + parse_host(Rest, Pieces); + + <<"@"/utf8, Rest@1/binary>> -> + Userinfo = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + {some, Userinfo}, + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_host(Rest@1, Pieces@1); + + <<""/utf8>> -> + parse_host(Original, Pieces); + + <<"/"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + <<"?"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + <<"#"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_userinfo_loop(Original, Rest@2, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 163). +-spec parse_authority_pieces(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_authority_pieces(String, Pieces) -> + parse_userinfo_loop(String, String, Pieces, 0). + +-file("src/gleam/uri.gleam", 150). +-spec parse_authority_with_slashes(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_authority_with_slashes(Uri_string, Pieces) -> + case Uri_string of + <<"//"/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<"//"/utf8, Rest/binary>> -> + parse_authority_pieces(Rest, Pieces); + + _ -> + parse_path(Uri_string, Pieces) + end. + +-file("src/gleam/uri.gleam", 91). +-spec parse_scheme_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_scheme_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"/"/utf8, _/binary>> when Size =:= 0 -> + parse_authority_with_slashes(Uri_string, Pieces); + + <<"/"/utf8, _/binary>> -> + Scheme = binary:part(Original, 0, Size), + Pieces@1 = {uri, + {some, string:lowercase(Scheme)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_authority_with_slashes(Uri_string, Pieces@1); + + <<"?"/utf8, Rest/binary>> when Size =:= 0 -> + parse_query_with_question_mark(Rest, Pieces); + + <<"?"/utf8, Rest@1/binary>> -> + Scheme@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + {some, string:lowercase(Scheme@1)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@1, Pieces@2); + + <<"#"/utf8, Rest@2/binary>> when Size =:= 0 -> + parse_fragment(Rest@2, Pieces); + + <<"#"/utf8, Rest@3/binary>> -> + Scheme@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + {some, string:lowercase(Scheme@2)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@3, Pieces@3); + + <<":"/utf8, _/binary>> when Size =:= 0 -> + {error, nil}; + + <<":"/utf8, Rest@4/binary>> -> + Scheme@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + {some, string:lowercase(Scheme@3)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_authority_with_slashes(Rest@4, Pieces@4); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Original, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@5} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_scheme_loop(Original, Rest@5, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 81). +?DOC( + " Parses a compliant URI string into the `Uri` type.\n" + " If the string is not a valid URI string then an error is returned.\n" + "\n" + " The opposite operation is `uri.to_string`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert parse(\"https://example.com:1234/a/b?query=true#fragment\")\n" + " == Ok(\n" + " Uri(\n" + " scheme: Some(\"https\"),\n" + " userinfo: None,\n" + " host: Some(\"example.com\"),\n" + " port: Some(1234),\n" + " path: \"/a/b\",\n" + " query: Some(\"query=true\"),\n" + " fragment: Some(\"fragment\")\n" + " )\n" + " )\n" + " ```\n" +). +-spec parse(binary()) -> {ok, uri()} | {error, nil}. +parse(Uri_string) -> + gleam_stdlib:uri_parse(Uri_string). + +-file("src/gleam/uri.gleam", 536). +?DOC( + " Parses an URL-encoded query string into a list of key value pairs.\n" + " Returns an error for invalid encoding.\n" + "\n" + " The opposite operation is `uri.query_to_string`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert parse_query(\"a=1&b=2\") == Ok([#(\"a\", \"1\"), #(\"b\", \"2\")])\n" + " ```\n" +). +-spec parse_query(binary()) -> {ok, list({binary(), binary()})} | {error, nil}. +parse_query(Query) -> + gleam_stdlib:parse_query(Query). + +-file("src/gleam/uri.gleam", 576). +?DOC( + " Encodes a string into a percent encoded representation.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert percent_encode(\"100% great\") == \"100%25%20great\"\n" + " ```\n" +). +-spec percent_encode(binary()) -> binary(). +percent_encode(Value) -> + gleam_stdlib:percent_encode(Value). + +-file("src/gleam/uri.gleam", 561). +-spec percent_encode_query(binary()) -> binary(). +percent_encode_query(Part) -> + _pipe = gleam_stdlib:percent_encode(Part), + gleam@string:replace(_pipe, <<"+"/utf8>>, <<"%2B"/utf8>>). + +-file("src/gleam/uri.gleam", 556). +-spec query_pair({binary(), binary()}) -> gleam@string_tree:string_tree(). +query_pair(Pair) -> + _pipe = [percent_encode_query(erlang:element(1, Pair)), + <<"="/utf8>>, + percent_encode_query(erlang:element(2, Pair))], + gleam_stdlib:identity(_pipe). + +-file("src/gleam/uri.gleam", 548). +?DOC( + " Encodes a list of key value pairs as a URI query string.\n" + "\n" + " The opposite operation is `uri.parse_query`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert query_to_string([#(\"a\", \"1\"), #(\"b\", \"2\")]) == \"a=1&b=2\"\n" + " ```\n" +). +-spec query_to_string(list({binary(), binary()})) -> binary(). +query_to_string(Query) -> + _pipe = Query, + _pipe@1 = gleam@list:map(_pipe, fun query_pair/1), + _pipe@2 = gleam@list:intersperse( + _pipe@1, + gleam_stdlib:identity(<<"&"/utf8>>) + ), + _pipe@3 = gleam_stdlib:identity(_pipe@2), + unicode:characters_to_binary(_pipe@3). + +-file("src/gleam/uri.gleam", 588). +?DOC( + " Decodes a percent encoded string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert percent_decode(\"100%25%20great+fun\") == Ok(\"100% great+fun\")\n" + " ```\n" +). +-spec percent_decode(binary()) -> {ok, binary()} | {error, nil}. +percent_decode(Value) -> + gleam_stdlib:percent_decode(Value). + +-file("src/gleam/uri.gleam", 609). +-spec remove_dot_segments_loop(list(binary()), list(binary())) -> list(binary()). +remove_dot_segments_loop(Input, Accumulator) -> + case Input of + [] -> + lists:reverse(Accumulator); + + [Segment | Rest] -> + Accumulator@5 = case {Segment, Accumulator} of + {<<""/utf8>>, Accumulator@1} -> + Accumulator@1; + + {<<"."/utf8>>, Accumulator@2} -> + Accumulator@2; + + {<<".."/utf8>>, []} -> + []; + + {<<".."/utf8>>, [_ | Accumulator@3]} -> + Accumulator@3; + + {Segment@1, Accumulator@4} -> + [Segment@1 | Accumulator@4] + end, + remove_dot_segments_loop(Rest, Accumulator@5) + end. + +-file("src/gleam/uri.gleam", 605). +-spec remove_dot_segments(list(binary())) -> list(binary()). +remove_dot_segments(Input) -> + remove_dot_segments_loop(Input, []). + +-file("src/gleam/uri.gleam", 601). +?DOC( + " Splits the path section of a URI into its constituent segments.\n" + "\n" + " Removes empty segments and resolves dot-segments as specified in\n" + " [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " assert path_segments(\"/users/1\") == [\"users\" ,\"1\"]\n" + " ```\n" +). +-spec path_segments(binary()) -> list(binary()). +path_segments(Path) -> + remove_dot_segments(gleam@string:split(Path, <<"/"/utf8>>)). + +-file("src/gleam/uri.gleam", 639). +?DOC( + " Encodes a `Uri` value as a URI string.\n" + "\n" + " The opposite operation is `uri.parse`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let uri = Uri(..empty, scheme: Some(\"https\"), host: Some(\"example.com\"))\n" + " assert to_string(uri) == \"https://example.com\"\n" + " ```\n" +). +-spec to_string(uri()) -> binary(). +to_string(Uri) -> + Parts = case erlang:element(8, Uri) of + {some, Fragment} -> + [<<"#"/utf8>>, Fragment]; + + none -> + [] + end, + Parts@1 = case erlang:element(7, Uri) of + {some, Query} -> + [<<"?"/utf8>>, Query | Parts]; + + none -> + Parts + end, + Parts@2 = [erlang:element(6, Uri) | Parts@1], + Parts@3 = case {erlang:element(4, Uri), + gleam_stdlib:string_starts_with(erlang:element(6, Uri), <<"/"/utf8>>)} of + {{some, Host}, false} when Host =/= <<""/utf8>> -> + [<<"/"/utf8>> | Parts@2]; + + {_, _} -> + Parts@2 + end, + Parts@4 = case {erlang:element(4, Uri), erlang:element(5, Uri)} of + {{some, _}, {some, Port}} -> + [<<":"/utf8>>, erlang:integer_to_binary(Port) | Parts@3]; + + {_, _} -> + Parts@3 + end, + Parts@5 = case {erlang:element(2, Uri), + erlang:element(3, Uri), + erlang:element(4, Uri)} of + {{some, S}, {some, U}, {some, H}} -> + [S, <<"://"/utf8>>, U, <<"@"/utf8>>, H | Parts@4]; + + {{some, S@1}, none, {some, H@1}} -> + [S@1, <<"://"/utf8>>, H@1 | Parts@4]; + + {{some, S@2}, {some, _}, none} -> + [S@2, <<":"/utf8>> | Parts@4]; + + {{some, S@2}, none, none} -> + [S@2, <<":"/utf8>> | Parts@4]; + + {none, none, {some, H@2}} -> + [<<"//"/utf8>>, H@2 | Parts@4]; + + {_, _, _} -> + Parts@4 + end, + erlang:list_to_binary(Parts@5). + +-file("src/gleam/uri.gleam", 682). +?DOC( + " Fetches the origin of a URI.\n" + "\n" + " Returns the origin of a uri as defined in\n" + " [RFC 6454](https://tools.ietf.org/html/rfc6454)\n" + "\n" + " The supported URI schemes are `http` and `https`.\n" + " URLs without a scheme will return `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(uri) = parse(\"https://example.com/path?foo#bar\")\n" + " assert origin(uri) == Ok(\"https://example.com\")\n" + " ```\n" +). +-spec origin(uri()) -> {ok, binary()} | {error, nil}. +origin(Uri) -> + {uri, Scheme, _, Host, Port, _, _, _} = Uri, + case {Host, Scheme} of + {{some, H}, {some, <<"https"/utf8>>}} when Port =:= {some, 443} -> + {ok, erlang:list_to_binary([<<"https://"/utf8>>, H])}; + + {{some, H@1}, {some, <<"http"/utf8>>}} when Port =:= {some, 80} -> + {ok, erlang:list_to_binary([<<"http://"/utf8>>, H@1])}; + + {{some, H@2}, {some, S}} when (S =:= <<"http"/utf8>>) orelse (S =:= <<"https"/utf8>>) -> + case Port of + {some, P} -> + {ok, + erlang:list_to_binary( + [S, + <<"://"/utf8>>, + H@2, + <<":"/utf8>>, + erlang:integer_to_binary(P)] + )}; + + none -> + {ok, erlang:list_to_binary([S, <<"://"/utf8>>, H@2])} + end; + + {_, _} -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 767). +-spec join_segments(list(binary())) -> binary(). +join_segments(Segments) -> + gleam@string:join([<<""/utf8>> | Segments], <<"/"/utf8>>). + +-file("src/gleam/uri.gleam", 763). +-spec drop_last(list(DDN)) -> list(DDN). +drop_last(Elements) -> + gleam@list:take(Elements, erlang:length(Elements) - 1). + +-file("src/gleam/uri.gleam", 705). +?DOC( + " Resolves a URI with respect to the given base URI.\n" + "\n" + " The base URI must be an absolute URI or this function will return an error.\n" + " The algorithm for merging URIs is described in\n" + " [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2).\n" +). +-spec merge(uri(), uri()) -> {ok, uri()} | {error, nil}. +merge(Base, Relative) -> + case Base of + {uri, {some, _}, _, {some, _}, _, _, _, _} -> + case Relative of + {uri, _, _, {some, _}, _, _, _, _} -> + Path = begin + _pipe = erlang:element(6, Relative), + _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), + _pipe@2 = remove_dot_segments(_pipe@1), + join_segments(_pipe@2) + end, + Resolved = {uri, + gleam@option:'or'( + erlang:element(2, Relative), + erlang:element(2, Base) + ), + none, + erlang:element(4, Relative), + gleam@option:'or'( + erlang:element(5, Relative), + erlang:element(5, Base) + ), + Path, + erlang:element(7, Relative), + erlang:element(8, Relative)}, + {ok, Resolved}; + + _ -> + {New_path, New_query} = case erlang:element(6, Relative) of + <<""/utf8>> -> + {erlang:element(6, Base), + gleam@option:'or'( + erlang:element(7, Relative), + erlang:element(7, Base) + )}; + + _ -> + Path_segments = case gleam_stdlib:string_starts_with( + erlang:element(6, Relative), + <<"/"/utf8>> + ) of + true -> + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ); + + false -> + _pipe@3 = erlang:element(6, Base), + _pipe@4 = gleam@string:split( + _pipe@3, + <<"/"/utf8>> + ), + _pipe@5 = drop_last(_pipe@4), + lists:append( + _pipe@5, + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ) + ) + end, + Path@1 = begin + _pipe@6 = Path_segments, + _pipe@7 = remove_dot_segments(_pipe@6), + join_segments(_pipe@7) + end, + {Path@1, erlang:element(7, Relative)} + end, + Resolved@1 = {uri, + erlang:element(2, Base), + none, + erlang:element(4, Base), + erlang:element(5, Base), + New_path, + New_query, + erlang:element(8, Relative)}, + {ok, Resolved@1} + end; + + _ -> + {error, nil} + end. diff --git a/ui/javascript/gleam_stdlib/gleam_stdlib.erl b/ui/javascript/gleam_stdlib/gleam_stdlib.erl new file mode 100644 index 0000000..9017bc7 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam_stdlib.erl @@ -0,0 +1,554 @@ +-module(gleam_stdlib). + +-export([ + map_get/2, iodata_append/2, identity/1, parse_int/1, parse_float/1, + less_than/2, string_pop_grapheme/1, string_pop_codeunit/1, + string_starts_with/2, wrap_list/1, string_ends_with/2, string_pad/4, + uri_parse/1, bit_array_slice/3, percent_encode/1, percent_decode/1, + base64_decode/1, parse_query/1, bit_array_concat/1, + base64_encode/2, tuple_get/2, classify_dynamic/1, print/1, + println/1, print_error/1, println_error/1, inspect/1, float_to_string/1, + int_from_base_string/2, utf_codepoint_list_to_string/1, contains_string/2, + crop_string/2, base16_encode/1, base16_decode/1, string_replace/3, slice/3, + bit_array_to_int_and_size/1, bit_array_pad_to_bytes/1, index/2, list/5, + dict/1, int/1, float/1, bit_array/1, is_null/1, string_remove_prefix/2, + string_remove_suffix/2 +]). + +%% Taken from OTP's uri_string module +-define(DEC2HEX(X), + if ((X) >= 0) andalso ((X) =< 9) -> (X) + $0; + ((X) >= 10) andalso ((X) =< 15) -> (X) + $A - 10 + end). + +%% Taken from OTP's uri_string module +-define(HEX2DEC(X), + if ((X) >= $0) andalso ((X) =< $9) -> (X) - $0; + ((X) >= $A) andalso ((X) =< $F) -> (X) - $A + 10; + ((X) >= $a) andalso ((X) =< $f) -> (X) - $a + 10 + end). + +-define(is_lowercase_char(X), + (X > 96 andalso X < 123)). +-define(is_underscore_char(X), + (X == 95)). +-define(is_digit_char(X), + (X > 47 andalso X < 58)). +-define(is_ascii_character(X), + (erlang:is_integer(X) andalso X >= 32 andalso X =< 126)). + +uppercase(X) -> X - 32. + +map_get(Map, Key) -> + case maps:find(Key, Map) of + error -> {error, nil}; + OkFound -> OkFound + end. + +iodata_append(Iodata, String) -> [Iodata, String]. + +identity(X) -> X. + +classify_dynamic(nil) -> <<"Nil">>; +classify_dynamic(null) -> <<"Nil">>; +classify_dynamic(undefined) -> <<"Nil">>; +classify_dynamic(X) when is_boolean(X) -> <<"Bool">>; +classify_dynamic(X) when is_atom(X) -> <<"Atom">>; +classify_dynamic(X) when is_binary(X) -> <<"String">>; +classify_dynamic(X) when is_bitstring(X) -> <<"BitArray">>; +classify_dynamic(X) when is_integer(X) -> <<"Int">>; +classify_dynamic(X) when is_float(X) -> <<"Float">>; +classify_dynamic(X) when is_list(X) -> <<"List">>; +classify_dynamic(X) when is_map(X) -> <<"Dict">>; +classify_dynamic(X) when is_tuple(X) -> <<"Array">>; +classify_dynamic(X) when is_reference(X) -> <<"Reference">>; +classify_dynamic(X) when is_pid(X) -> <<"Pid">>; +classify_dynamic(X) when is_port(X) -> <<"Port">>; +classify_dynamic(X) when is_function(X) -> <<"Function">>; +classify_dynamic(_) -> <<"Unknown">>. + +tuple_get(_tup, Index) when Index < 0 -> {error, nil}; +tuple_get(Data, Index) when Index >= tuple_size(Data) -> {error, nil}; +tuple_get(Data, Index) -> {ok, element(Index + 1, Data)}. + +int_from_base_string(String, Base) -> + try binary_to_integer(String, Base) of + Int when is_integer(Int) -> {ok, Int}; + _ -> {error, nil} + catch _:_ -> {error, nil} + end. + +parse_int(String) -> + try binary_to_integer(String) of + Int when is_integer(Int) -> {ok, Int}; + _ -> {error, nil} + catch _:_ -> {error, nil} + end. + +parse_float(String) -> + try binary_to_float(String) of + Float when is_float(Float) -> {ok, Float}; + _ -> {error, nil} + catch _:_ -> {error, nil} + end. + +less_than(Lhs, Rhs) -> + Lhs < Rhs. + +string_starts_with(_, <<>>) -> true; +string_starts_with(String, Prefix) when byte_size(Prefix) > byte_size(String) -> false; +string_starts_with(String, Prefix) -> + PrefixSize = byte_size(Prefix), + Prefix == binary_part(String, 0, PrefixSize). + +string_ends_with(_, <<>>) -> true; +string_ends_with(String, Suffix) when byte_size(Suffix) > byte_size(String) -> false; +string_ends_with(String, Suffix) -> + SuffixSize = byte_size(Suffix), + Suffix == binary_part(String, byte_size(String) - SuffixSize, SuffixSize). + +string_pad(String, Length, Dir, PadString) -> + Chars = string:pad(String, Length, Dir, binary_to_list(PadString)), + case unicode:characters_to_binary(Chars) of + Bin when is_binary(Bin) -> Bin; + Error -> erlang:error({gleam_error, {string_invalid_utf8, Error}}) + end. + +string_pop_grapheme(String) -> + case string:next_grapheme(String) of + [ Next | Rest ] when is_binary(Rest) -> + {ok, {unicode:characters_to_binary([Next]), Rest}}; + + [ Next | Rest ] -> + {ok, {unicode:characters_to_binary([Next]), unicode:characters_to_binary(Rest)}}; + + _ -> {error, nil} + end. + +string_pop_codeunit(<>) -> {Cp, Rest}; +string_pop_codeunit(Binary) -> {0, Binary}. + +bit_array_pad_to_bytes(Bin) -> + case erlang:bit_size(Bin) rem 8 of + 0 -> Bin; + TrailingBits -> + PaddingBits = 8 - TrailingBits, + <> + end. + +bit_array_concat(BitArrays) -> + list_to_bitstring(BitArrays). + +-if(?OTP_RELEASE >= 26). +base64_encode(Bin, Padding) -> + PaddedBin = bit_array_pad_to_bytes(Bin), + base64:encode(PaddedBin, #{padding => Padding}). +-else. +base64_encode(_Bin, _Padding) -> + erlang:error(<<"Erlang OTP/26 or higher is required to use base64:encode">>). +-endif. + +bit_array_slice(Bin, Pos, Len) -> + try {ok, binary:part(Bin, Pos, Len)} + catch error:badarg -> {error, nil} + end. + +base64_decode(S) -> + try {ok, base64:decode(S)} + catch error:_ -> {error, nil} + end. + +wrap_list(X) when is_list(X) -> X; +wrap_list(X) -> [X]. + +parse_query(Query) -> + case uri_string:dissect_query(Query) of + {error, _, _} -> {error, nil}; + Pairs -> + Pairs1 = lists:map(fun + ({K, true}) -> {K, <<"">>}; + (Pair) -> Pair + end, Pairs), + {ok, Pairs1} + end. + +percent_encode(B) -> percent_encode(B, <<>>). +percent_encode(<<>>, Acc) -> + Acc; +percent_encode(<>, Acc) -> + case percent_ok(H) of + true -> + percent_encode(T, <>); + false -> + <> = <>, + percent_encode(T, <>) + end. + +percent_decode(Cs) -> percent_decode(Cs, <<>>). +percent_decode(<<$%, C0, C1, Cs/binary>>, Acc) -> + case is_hex_digit(C0) andalso is_hex_digit(C1) of + true -> + B = ?HEX2DEC(C0)*16+?HEX2DEC(C1), + percent_decode(Cs, <>); + false -> + {error, nil} + end; +percent_decode(<>, Acc) -> + percent_decode(Cs, <>); +percent_decode(<<>>, Acc) -> + check_utf8(Acc). + +percent_ok($!) -> true; +percent_ok($$) -> true; +percent_ok($') -> true; +percent_ok($() -> true; +percent_ok($)) -> true; +percent_ok($*) -> true; +percent_ok($+) -> true; +percent_ok($-) -> true; +percent_ok($.) -> true; +percent_ok($_) -> true; +percent_ok($~) -> true; +percent_ok(C) when $0 =< C, C =< $9 -> true; +percent_ok(C) when $A =< C, C =< $Z -> true; +percent_ok(C) when $a =< C, C =< $z -> true; +percent_ok(_) -> false. + +is_hex_digit(C) -> + ($0 =< C andalso C =< $9) orelse ($a =< C andalso C =< $f) orelse ($A =< C andalso C =< $F). + +check_utf8(Cs) -> + case unicode:characters_to_list(Cs) of + {incomplete, _, _} -> {error, nil}; + {error, _, _} -> {error, nil}; + _ -> {ok, Cs} + end. + +uri_parse(String) -> + case uri_string:parse(String) of + {error, _, _} -> {error, nil}; + Uri -> + Port = + try maps:get(port, Uri) of + undefined -> none; + Value -> {some, Value} + catch _:_ -> none + end, + {ok, {uri, + maps_get_optional_lowercase(Uri, scheme), + maps_get_optional(Uri, userinfo), + maps_get_optional(Uri, host), + Port, + maps_get_or(Uri, path, <<>>), + maps_get_optional(Uri, query), + maps_get_optional(Uri, fragment) + }} + end. + +maps_get_optional_lowercase(Map, Key) -> + try {some, string:lowercase(maps:get(Key, Map))} + catch _:_ -> none + end. + +maps_get_optional(Map, Key) -> + try {some, maps:get(Key, Map)} + catch _:_ -> none + end. + +maps_get_or(Map, Key, Default) -> + try maps:get(Key, Map) + catch _:_ -> Default + end. + +print(String) -> + io:put_chars(String), + nil. + +println(String) -> + io:put_chars([String, $\n]), + nil. + +print_error(String) -> + io:put_chars(standard_error, String), + nil. + +println_error(String) -> + io:put_chars(standard_error, [String, $\n]), + nil. + +inspect(true) -> + "True"; +inspect(false) -> + "False"; +inspect(nil) -> + "Nil"; +inspect(Data) when is_map(Data) -> + Fields = [ + [<<"#(">>, inspect(Key), <<", ">>, inspect(Value), <<")">>] + || {Key, Value} <- maps:to_list(Data) + ], + ["dict.from_list([", lists:join(", ", Fields), "])"]; +inspect(Atom) when is_atom(Atom) -> + erlang:element(2, inspect_atom(Atom)); +inspect(Any) when is_integer(Any) -> + erlang:integer_to_list(Any); +inspect(Any) when is_float(Any) -> + io_lib_format:fwrite_g(Any); +inspect(Binary) when is_binary(Binary) -> + case inspect_maybe_utf8_string(Binary, <<>>) of + {ok, InspectedUtf8String} -> InspectedUtf8String; + {error, not_a_utf8_string} -> + Segments = [erlang:integer_to_list(X) || <> <= Binary], + ["<<", lists:join(", ", Segments), ">>"] + end; +inspect(Bits) when is_bitstring(Bits) -> + inspect_bit_array(Bits); +inspect(List) when is_list(List) -> + case inspect_list(List, true) of + {charlist, _} -> ["charlist.from_string(\"", list_to_binary(List), "\")"]; + {proper, Elements} -> ["[", Elements, "]"]; + {improper, Elements} -> ["//erl([", Elements, "])"] + end; +inspect(Any) when is_tuple(Any) % Record constructors + andalso is_atom(element(1, Any)) + andalso element(1, Any) =/= false + andalso element(1, Any) =/= true + andalso element(1, Any) =/= nil +-> + [Atom | ArgsList] = erlang:tuple_to_list(Any), + InspectedArgs = lists:map(fun inspect/1, ArgsList), + case inspect_atom(Atom) of + {gleam_atom, GleamAtom} -> + Args = lists:join(<<", ">>, InspectedArgs), + [GleamAtom, "(", Args, ")"]; + {erlang_atom, ErlangAtom} -> + Args = lists:join(<<", ">>, [ErlangAtom | InspectedArgs]), + ["#(", Args, ")"] + end; +inspect(Tuple) when is_tuple(Tuple) -> + Elements = lists:map(fun inspect/1, erlang:tuple_to_list(Tuple)), + ["#(", lists:join(", ", Elements), ")"]; +inspect(Any) when is_function(Any) -> + {arity, Arity} = erlang:fun_info(Any, arity), + ArgsAsciiCodes = lists:seq($a, $a + Arity - 1), + Args = lists:join(<<", ">>, + lists:map(fun(Arg) -> <> end, ArgsAsciiCodes) + ), + ["//fn(", Args, ") { ... }"]; +inspect(Any) -> + ["//erl(", io_lib:format("~p", [Any]), ")"]. + +inspect_atom(Atom) -> + Binary = erlang:atom_to_binary(Atom), + case inspect_maybe_gleam_atom(Binary, none, <<>>) of + {ok, Inspected} -> {gleam_atom, Inspected}; + {error, _} -> {erlang_atom, ["atom.create(\"", Binary, "\")"]} + end. + +inspect_maybe_gleam_atom(<<>>, none, _) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, none, _) when ?is_digit_char(First) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, none, _) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_">>, _PrevChar, _Acc) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, $_, _Acc) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, _PrevChar, _Acc) + when not (?is_lowercase_char(First) orelse ?is_underscore_char(First) orelse ?is_digit_char(First)) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, none, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<<"_", Rest/binary>>, _PrevChar, Acc) -> + inspect_maybe_gleam_atom(Rest, $_, Acc); +inspect_maybe_gleam_atom(<>, $_, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<>, _PrevChar, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<<>>, _PrevChar, Acc) -> + {ok, Acc}; +inspect_maybe_gleam_atom(A, B, C) -> + erlang:display({A, B, C}), + throw({gleam_error, A, B, C}). + +inspect_list([], _) -> + {proper, []}; +inspect_list([First], true) when ?is_ascii_character(First) -> + {charlist, nil}; +inspect_list([First], _) -> + {proper, [inspect(First)]}; +inspect_list([First | Rest], ValidCharlist) when is_list(Rest) -> + StillValidCharlist = ValidCharlist andalso ?is_ascii_character(First), + {Kind, Inspected} = inspect_list(Rest, StillValidCharlist), + {Kind, [inspect(First), <<", ">> | Inspected]}; +inspect_list([First | ImproperTail], _) -> + {improper, [inspect(First), <<" | ">>, inspect(ImproperTail)]}. + +inspect_bit_array(Bits) -> + Text = inspect_bit_array(Bits, <<"<<">>), + <>">>. + +inspect_bit_array(<<>>, Acc) -> + Acc; +inspect_bit_array(<>, Acc) -> + inspect_bit_array(Rest, append_segment(Acc, erlang:integer_to_binary(X))); +inspect_bit_array(Rest, Acc) -> + Size = bit_size(Rest), + <> = Rest, + X1 = erlang:integer_to_binary(X), + Size1 = erlang:integer_to_binary(Size), + Segment = <>, + inspect_bit_array(<<>>, append_segment(Acc, Segment)). + +bit_array_to_int_and_size(A) -> + Size = bit_size(A), + <> = A, + {A1, Size}. + +append_segment(<<"<<">>, Segment) -> + <<"<<", Segment/binary>>; +append_segment(Acc, Segment) -> + <>. + + +inspect_maybe_utf8_string(Binary, Acc) -> + case Binary of + <<>> -> {ok, <<$", Acc/binary, $">>}; + <> -> + Escaped = case First of + $" -> <<$\\, $">>; + $\\ -> <<$\\, $\\>>; + $\r -> <<$\\, $r>>; + $\n -> <<$\\, $n>>; + $\t -> <<$\\, $t>>; + $\f -> <<$\\, $f>>; + X when X > 126, X < 160 -> convert_to_u(X); + X when X < 32 -> convert_to_u(X); + Other -> <> + end, + inspect_maybe_utf8_string(Rest, <>); + _ -> {error, not_a_utf8_string} + end. + +convert_to_u(Code) -> + list_to_binary(io_lib:format("\\u{~4.16.0B}", [Code])). + +float_to_string(Float) when is_float(Float) -> + erlang:iolist_to_binary(io_lib_format:fwrite_g(Float)). + +utf_codepoint_list_to_string(List) -> + case unicode:characters_to_binary(List) of + {error, _} -> erlang:error({gleam_error, {string_invalid_utf8, List}}); + Binary -> Binary + end. + +crop_string(String, Prefix) -> + case string:find(String, Prefix) of + nomatch -> String; + New -> New + end. + +contains_string(String, Substring) -> + is_bitstring(string:find(String, Substring)). + +base16_encode(Bin) -> + PaddedBin = bit_array_pad_to_bytes(Bin), + binary:encode_hex(PaddedBin). + +base16_decode(String) -> + try + {ok, binary:decode_hex(String)} + catch + _:_ -> {error, nil} + end. + +string_replace(String, Pattern, Replacement) -> + string:replace(String, Pattern, Replacement, all). + +slice(String, Index, Length) -> + case string:slice(String, Index, Length) of + X when is_binary(X) -> X; + X when is_list(X) -> unicode:characters_to_binary(X) + end. + +index([X | _], 0) -> + {ok, {some, X}}; +index([_, X | _], 1) -> + {ok, {some, X}}; +index([_, _, X | _], 2) -> + {ok, {some, X}}; +index([_, _, _, X | _], 3) -> + {ok, {some, X}}; +index([_, _, _, _, X | _], 4) -> + {ok, {some, X}}; +index([_, _, _, _, _, X | _], 5) -> + {ok, {some, X}}; +index([_, _, _, _, _, _, X | _], 6) -> + {ok, {some, X}}; +index([_, _, _, _, _, _, _, X | _], 7) -> + {ok, {some, X}}; +index(Tuple, Index) when is_tuple(Tuple) andalso is_integer(Index) -> + {ok, try + {some, element(Index + 1, Tuple)} + catch _:_ -> + none + end}; +index(Map, Key) when is_map(Map) -> + {ok, try + {some, maps:get(Key, Map)} + catch _:_ -> + none + end}; +index(_, Index) when is_integer(Index) -> + {error, <<"Indexable">>}; +index(_, _) -> + {error, <<"Dict">>}. + +list(T, A, B, C, D) when is_tuple(T) -> + list(tuple_to_list(T), A, B, C, D); +list([], _, _, _, Acc) -> + {lists:reverse(Acc), []}; +list([X | Xs], Decode, PushPath, Index, Acc) -> + {Out, Errors} = Decode(X), + case Errors of + [] -> list(Xs, Decode, PushPath, Index + 1, [Out | Acc]); + _ -> PushPath({[], Errors}, integer_to_binary(Index)) + end; +list(Unexpected, _, _, _, []) -> + Found = gleam@dynamic:classify(Unexpected), + Error = {decode_error, <<"List"/utf8>>, Found, []}, + {[], [Error]}; +list(_, _, _, _, Acc) -> + {lists:reverse(Acc), []}. + +dict(#{} = Data) -> {ok, Data}; +dict(_) -> {error, nil}. + +int(I) when is_integer(I) -> {ok, I}; +int(_) -> {error, 0}. + +float(F) when is_float(F) -> {ok, F}; +float(_) -> {error, 0.0}. + +bit_array(B) when is_bitstring(B) -> {ok, B}; +bit_array(_) -> {error, <<>>}. + +is_null(X) -> + X =:= undefined orelse X =:= null orelse X =:= nil. + +string_remove_prefix(String, Prefix) -> + PrefixSize = byte_size(Prefix), + case String of + <> -> Suffix; + _ -> String + end. + +string_remove_suffix(String, Suffix) -> + StringSize = byte_size(String), + SuffixSize = byte_size(Suffix), + Offset = StringSize - SuffixSize, + case String of + <> -> Prefix; + _ -> String + end. diff --git a/ui/javascript/gleam_stdlib/gleam_stdlib.mjs b/ui/javascript/gleam_stdlib/gleam_stdlib.mjs new file mode 100644 index 0000000..96582b1 --- /dev/null +++ b/ui/javascript/gleam_stdlib/gleam_stdlib.mjs @@ -0,0 +1,1150 @@ +import { + BitArray, + List$Empty, + List$NonEmpty, + List$isEmpty, + List$isNonEmpty, + Result$Ok, + Result$Error, + Result$isOk, + Result$isError, + UtfCodepoint, + stringBits, + toBitArray, + bitArraySlice, + CustomType, +} from "./gleam.mjs"; +import { Some, None } from "./gleam/option.mjs"; +import { + default as Dict, + fold as dict_fold, + get as dict_get, + from as dict_from_iterable, +} from "./dict.mjs"; +import { classify } from "./gleam/dynamic.mjs"; +import { DecodeError$DecodeError } from "./gleam/dynamic/decode.mjs"; + +const Nil = undefined; + +export function identity(x) { + return x; +} + +export function parse_int(value) { + if (/^[-+]?(\d+)$/.test(value)) { + return Result$Ok(parseInt(value)); + } else { + return Result$Error(Nil); + } +} + +export function parse_float(value) { + if (/^[-+]?(\d+)\.(\d+)([eE][-+]?\d+)?$/.test(value)) { + return Result$Ok(parseFloat(value)); + } else { + return Result$Error(Nil); + } +} + +export function to_string(term) { + return term.toString(); +} + +export function int_to_base_string(int, base) { + return int.toString(base).toUpperCase(); +} + +const int_base_patterns = { + 2: /[^0-1]/, + 3: /[^0-2]/, + 4: /[^0-3]/, + 5: /[^0-4]/, + 6: /[^0-5]/, + 7: /[^0-6]/, + 8: /[^0-7]/, + 9: /[^0-8]/, + 10: /[^0-9]/, + 11: /[^0-9a]/, + 12: /[^0-9a-b]/, + 13: /[^0-9a-c]/, + 14: /[^0-9a-d]/, + 15: /[^0-9a-e]/, + 16: /[^0-9a-f]/, + 17: /[^0-9a-g]/, + 18: /[^0-9a-h]/, + 19: /[^0-9a-i]/, + 20: /[^0-9a-j]/, + 21: /[^0-9a-k]/, + 22: /[^0-9a-l]/, + 23: /[^0-9a-m]/, + 24: /[^0-9a-n]/, + 25: /[^0-9a-o]/, + 26: /[^0-9a-p]/, + 27: /[^0-9a-q]/, + 28: /[^0-9a-r]/, + 29: /[^0-9a-s]/, + 30: /[^0-9a-t]/, + 31: /[^0-9a-u]/, + 32: /[^0-9a-v]/, + 33: /[^0-9a-w]/, + 34: /[^0-9a-x]/, + 35: /[^0-9a-y]/, + 36: /[^0-9a-z]/, +}; + +export function int_from_base_string(string, base) { + if (int_base_patterns[base].test(string.replace(/^-/, "").toLowerCase())) { + return Result$Error(Nil); + } + + const result = parseInt(string, base); + + if (isNaN(result)) { + return Result$Error(Nil); + } + + return Result$Ok(result); +} + +export function string_replace(string, target, substitute) { + return string.replaceAll(target, substitute); +} + +export function string_reverse(string) { + return [...string].reverse().join(""); +} + +export function string_length(string) { + if (string === "") { + return 0; + } + const iterator = graphemes_iterator(string); + if (iterator) { + let i = 0; + for (const _ of iterator) { + i++; + } + return i; + } else { + return string.match(/./gsu).length; + } +} + +export function graphemes(string) { + const iterator = graphemes_iterator(string); + if (iterator) { + return arrayToList(Array.from(iterator).map((item) => item.segment)); + } else { + return arrayToList(string.match(/./gsu)); + } +} + +let segmenter = undefined; + +function graphemes_iterator(string) { + if (globalThis.Intl && Intl.Segmenter) { + segmenter ||= new Intl.Segmenter(); + return segmenter.segment(string)[Symbol.iterator](); + } +} + +export function pop_grapheme(string) { + let first; + const iterator = graphemes_iterator(string); + if (iterator) { + first = iterator.next().value?.segment; + } else { + first = string.match(/./su)?.[0]; + } + if (first) { + return Result$Ok([first, string.slice(first.length)]); + } else { + return Result$Error(Nil); + } +} + +export function pop_codeunit(str) { + return [str.charCodeAt(0) | 0, str.slice(1)]; +} + +export function lowercase(string) { + return string.toLowerCase(); +} + +export function uppercase(string) { + return string.toUpperCase(); +} + +export function less_than(a, b) { + return a < b; +} + +export function add(a, b) { + return a + b; +} + +export function split(xs, pattern) { + return arrayToList(xs.split(pattern)); +} + +export function concat(xs) { + let result = ""; + for (const x of xs) { + result = result + x; + } + return result; +} + +export function length(data) { + return data.length; +} + +export function string_byte_slice(string, index, length) { + return string.slice(index, index + length); +} + +export function string_grapheme_slice(string, idx, len) { + if (len <= 0 || idx >= string.length) { + return ""; + } + + const iterator = graphemes_iterator(string); + if (iterator) { + while (idx-- > 0) { + iterator.next(); + } + + let result = ""; + + while (len-- > 0) { + const v = iterator.next().value; + if (v === undefined) { + break; + } + + result += v.segment; + } + + return result; + } else { + return string + .match(/./gsu) + .slice(idx, idx + len) + .join(""); + } +} + +export function string_codeunit_slice(str, from, length) { + return str.slice(from, from + length); +} +export function crop_string(string, substring) { + return string.substring(string.indexOf(substring)); +} + +export function contains_string(haystack, needle) { + return haystack.indexOf(needle) >= 0; +} + +export function starts_with(haystack, needle) { + return haystack.startsWith(needle); +} + +export function ends_with(haystack, needle) { + return haystack.endsWith(needle); +} + +export function split_once(haystack, needle) { + const index = haystack.indexOf(needle); + if (index >= 0) { + const before = haystack.slice(0, index); + const after = haystack.slice(index + needle.length); + return Result$Ok([before, after]); + } else { + return Result$Error(Nil); + } +} + +const unicode_whitespaces = [ + "\u0020", // Space + "\u0009", // Horizontal tab + "\u000A", // Line feed + "\u000B", // Vertical tab + "\u000C", // Form feed + "\u000D", // Carriage return + "\u0085", // Next line + "\u2028", // Line separator + "\u2029", // Paragraph separator +].join(""); + +const trim_start_regex = /* @__PURE__ */ new RegExp( + `^[${unicode_whitespaces}]*`, +); +const trim_end_regex = /* @__PURE__ */ new RegExp(`[${unicode_whitespaces}]*$`); + +export function trim_start(string) { + return string.replace(trim_start_regex, ""); +} + +export function trim_end(string) { + return string.replace(trim_end_regex, ""); +} + +export function bit_array_from_string(string) { + return toBitArray([stringBits(string)]); +} + +export function bit_array_bit_size(bit_array) { + return bit_array.bitSize; +} + +export function bit_array_byte_size(bit_array) { + return bit_array.byteSize; +} + +export function bit_array_pad_to_bytes(bit_array) { + const trailingBitsCount = bit_array.bitSize % 8; + + // If the bit array is a whole number of bytes it can be returned unchanged + if (trailingBitsCount === 0) { + return bit_array; + } + + const finalByte = bit_array.byteAt(bit_array.byteSize - 1); + + // The required final byte has its unused trailing bits set to zero + const unusedBitsCount = 8 - trailingBitsCount; + const correctFinalByte = (finalByte >> unusedBitsCount) << unusedBitsCount; + + // If the unused bits in the final byte are already set to zero then the + // existing buffer can be re-used, avoiding a copy + if (finalByte === correctFinalByte) { + return new BitArray( + bit_array.rawBuffer, + bit_array.byteSize * 8, + bit_array.bitOffset, + ); + } + + // Copy the bit array into a new aligned buffer and set the correct final byte + const buffer = new Uint8Array(bit_array.byteSize); + for (let i = 0; i < buffer.length - 1; i++) { + buffer[i] = bit_array.byteAt(i); + } + buffer[buffer.length - 1] = correctFinalByte; + + return new BitArray(buffer); +} + +export function bit_array_concat(bit_arrays) { + return toBitArray(bit_arrays.toArray()); +} + +export function console_log(term) { + console.log(term); +} + +export function console_error(term) { + console.error(term); +} + +export function crash(message) { + throw new globalThis.Error(message); +} + +export function bit_array_to_string(bit_array) { + // If the bit array isn't a whole number of bytes then return an error + if (bit_array.bitSize % 8 !== 0) { + return Result$Error(Nil); + } + + try { + const decoder = new TextDecoder("utf-8", { fatal: true }); + + if (bit_array.bitOffset === 0) { + return Result$Ok(decoder.decode(bit_array.rawBuffer)); + } else { + // The input data isn't aligned, so copy it into a new aligned buffer so + // that TextDecoder can be used + const buffer = new Uint8Array(bit_array.byteSize); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = bit_array.byteAt(i); + } + return Result$Ok(decoder.decode(buffer)); + } + } catch { + return Result$Error(Nil); + } +} + +export function print(string) { + if (typeof process === "object" && process.stdout?.write) { + process.stdout.write(string); // We can write without a trailing newline + } else if (typeof Deno === "object") { + Deno.stdout.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline + } else { + console.log(string); // We're in a browser. Newlines are mandated + } +} + +export function print_error(string) { + if (typeof process === "object" && process.stderr?.write) { + process.stderr.write(string); // We can write without a trailing newline + } else if (typeof Deno === "object") { + Deno.stderr.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline + } else { + console.error(string); // We're in a browser. Newlines are mandated + } +} + +export function print_debug(string) { + if (typeof process === "object" && process.stderr?.write) { + process.stderr.write(string + "\n"); // If we're in Node.js, use `stderr` + } else if (typeof Deno === "object") { + Deno.stderr.writeSync(new TextEncoder().encode(string + "\n")); // If we're in Deno, use `stderr` + } else { + console.log(string); // Otherwise, use `console.log` (so that it doesn't look like an error) + } +} + +export function ceiling(float) { + return Math.ceil(float); +} + +export function floor(float) { + return Math.floor(float); +} + +export function round(float) { + return Math.round(float); +} + +export function truncate(float) { + return Math.trunc(float); +} + +export function power(base, exponent) { + // It is checked in Gleam that: + // - The base is non-negative and that the exponent is not fractional. + // - The base is non-zero and the exponent is non-negative (otherwise + // the result will essentially be division by zero). + // It can thus be assumed that valid input is passed to the Math.pow + // function and a NaN or Infinity value will not be produced. + return Math.pow(base, exponent); +} + +export function random_uniform() { + const random_uniform_result = Math.random(); + // With round-to-nearest-even behavior, the ranges claimed for the functions below + // (excluding the one for Math.random() itself) aren't exact. + // If extremely large bounds are chosen (2^53 or higher), + // it's possible in extremely rare cases to calculate the usually-excluded upper bound. + // Note that as numbers in JavaScript are IEEE 754 floating point numbers + // See: + // Because of this, we just loop 'until' we get a valid result where 0.0 <= x < 1.0: + if (random_uniform_result === 1.0) { + return random_uniform(); + } + return random_uniform_result; +} + +export function bit_array_slice(bits, position, length) { + const start = Math.min(position, position + length); + const end = Math.max(position, position + length); + + if (start < 0 || end * 8 > bits.bitSize) { + return Result$Error(Nil); + } + + return Result$Ok(bitArraySlice(bits, start * 8, end * 8)); +} + +export function codepoint(int) { + return new UtfCodepoint(int); +} + +export function string_to_codepoint_integer_list(string) { + return arrayToList(Array.from(string).map((item) => item.codePointAt(0))); +} + +export function utf_codepoint_list_to_string(utf_codepoint_integer_list) { + return utf_codepoint_integer_list + .toArray() + .map((x) => String.fromCodePoint(x.value)) + .join(""); +} + +export function utf_codepoint_to_int(utf_codepoint) { + return utf_codepoint.value; +} + +function unsafe_percent_decode(string) { + return decodeURIComponent(string || ""); +} + +function unsafe_percent_decode_query(string) { + return decodeURIComponent((string || "").replaceAll("+", " ")); +} + +export function percent_decode(string) { + try { + return Result$Ok(unsafe_percent_decode(string)); + } catch { + return Result$Error(Nil); + } +} + +export function percent_encode(string) { + return encodeURIComponent(string).replaceAll("%2B", "+"); +} + +export function parse_query(query) { + try { + const pairs = []; + for (const section of query.split("&")) { + const [key, value] = section.split("="); + if (!key) continue; + + const decodedKey = unsafe_percent_decode_query(key); + const decodedValue = unsafe_percent_decode_query(value); + pairs.push([decodedKey, decodedValue]); + } + return Result$Ok(arrayToList(pairs)); + } catch { + return Result$Error(Nil); + } +} + +const b64EncodeLookup = [ + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, +]; + +let b64TextDecoder; + +// Implementation based on https://github.com/mitschabaude/fast-base64/blob/main/js.js +export function base64_encode(bit_array, padding) { + b64TextDecoder ??= new TextDecoder(); + + bit_array = bit_array_pad_to_bytes(bit_array); + + const m = bit_array.byteSize; + const k = m % 3; + const n = Math.floor(m / 3) * 4 + (k && k + 1); + const N = Math.ceil(m / 3) * 4; + const encoded = new Uint8Array(N); + + for (let i = 0, j = 0; j < m; i += 4, j += 3) { + const y = + (bit_array.byteAt(j) << 16) + + (bit_array.byteAt(j + 1) << 8) + + (bit_array.byteAt(j + 2) | 0); + + encoded[i] = b64EncodeLookup[y >> 18]; + encoded[i + 1] = b64EncodeLookup[(y >> 12) & 0x3f]; + encoded[i + 2] = b64EncodeLookup[(y >> 6) & 0x3f]; + encoded[i + 3] = b64EncodeLookup[y & 0x3f]; + } + + let base64 = b64TextDecoder.decode(new Uint8Array(encoded.buffer, 0, n)); + + if (padding) { + if (k === 1) { + base64 += "=="; + } else if (k === 2) { + base64 += "="; + } + } + + return base64; +} + +// From https://developer.mozilla.org/en-US/docs/Glossary/Base64 +export function base64_decode(sBase64) { + try { + const binString = atob(sBase64); + const length = binString.length; + const array = new Uint8Array(length); + for (let i = 0; i < length; i++) { + array[i] = binString.charCodeAt(i); + } + return Result$Ok(new BitArray(array)); + } catch { + return Result$Error(Nil); + } +} + +export function classify_dynamic(data) { + if (typeof data === "string") { + return "String"; + } else if (typeof data === "boolean") { + return "Bool"; + } else if (isResult(data)) { + return "Result"; + } else if (isList(data)) { + return "List"; + } else if (data instanceof BitArray) { + return "BitArray"; + } else if (data instanceof Dict) { + return "Dict"; + } else if (Number.isInteger(data)) { + return "Int"; + } else if (Array.isArray(data)) { + return `Array`; + } else if (typeof data === "number") { + return "Float"; + } else if (data === null) { + return "Nil"; + } else if (data === undefined) { + return "Nil"; + } else { + const type = typeof data; + return type.charAt(0).toUpperCase() + type.slice(1); + } +} + +export function byte_size(string) { + return new TextEncoder().encode(string).length; +} + +// In JavaScript, bitwise operations convert numbers to a sequence of 32 bits, +// while Erlang uses arbitrary precision integers. +// +// To get around this, every function here follows this pattern: +// +// 1. If both values fit in the 32-bit signed integer range, use the standard +// JavaScript bitwise operators directly. +// +// Note: For bitwise_shift_left, the result also needs to fit in 32 bits, +// so we use floating-point multiplication instead. +// +// 2. If either value falls outside the safe integer range (-2^53, 2^53), +// fall back to BigInt arithmetic, then downcast the result back to a Number. +// +// 3. Otherwise (safe integers outside the 32-bit range), we split the operation +// across the high 21 bits and low 32 bits individually: +// +// x1 $ x2 = ((hi(x1) $ hi(x2)) << 32) | (lo(x1) $ lo(x2)) +// +// where `$` is a bitwise operator. +// +// We split both values into a `hi` and a `lo` part: +// +// hi(x) = Math.floor(x / 2^32) the upper 21 bits +// lo(x) = x >>> 0 the lower 32 bits (as unsigned) +// +// For `hi`, we use that shifts are equal to multiplication/division with +// powers of two to get around the 32-bit range limitation. Math.floor is +// used instead of truncation since arithmetic right shift fills with the +// sign bit. For negative numbers, the discarded bits were non-zero +// (representing a positive fractional part), so discarding them makes the +// result strictly more negative, i.e. rounding away from 0. +// +// This works because bitwise operators are distributive across bit ranges: +// +// x1 $ x2 = (hi(x1) $ hi(x2)) << 32 | (lo(x1) $ lo(x2)) +// = (hi(x1) $ hi(x2)) * 2^32 + (lo(x1) $ lo(x2)) +// +// JavaScript bitwise operators truncate inputs to signed 32-bit integers, +// so `x1 $ x2` already computes `lo(x1) $ lo(x2)` — we just need to +// reinterpret the signed result as unsigned using `>>> 0`: +// +// lo(x1) $ lo(x2) = (x1 $ x2) >>> 0 = lo(x1 $ x2) +// => x1 $ x2 = (hi(x1) $ hi(x2)) * 2^32 + lo(x1 $ x2). +// +const MIN_I32 = -(2 ** 31); // -2147483648 +const MAX_I32 = 2 ** 31 - 1; // 2147483647 +const U32 = 2 ** 32; +const MAX_SAFE = Number.MAX_SAFE_INTEGER; +const MIN_SAFE = Number.MIN_SAFE_INTEGER; + +export function bitwise_and(x, y) { + if (x >= MIN_I32 && x <= MAX_I32 && y >= MIN_I32 && y <= MAX_I32) + return x & y; + if (x < MIN_SAFE || x > MAX_SAFE || y < MIN_SAFE || y > MAX_SAFE) + return Number(BigInt(x) & BigInt(y)); + + return (Math.floor(x / U32) & Math.floor(y / U32)) * U32 + ((x & y) >>> 0); +} + +export function bitwise_or(x, y) { + if (x >= MIN_I32 && x <= MAX_I32 && y >= MIN_I32 && y <= MAX_I32) + return x | y; + if (x < MIN_SAFE || x > MAX_SAFE || y < MIN_SAFE || y > MAX_SAFE) + return Number(BigInt(x) | BigInt(y)); + + return (Math.floor(x / U32) | Math.floor(y / U32)) * U32 + ((x | y) >>> 0); +} + +export function bitwise_exclusive_or(x, y) { + if (x >= MIN_I32 && x <= MAX_I32 && y >= MIN_I32 && y <= MAX_I32) + return x ^ y; + if (x < MIN_SAFE || x > MAX_SAFE || y < MIN_SAFE || y > MAX_SAFE) + return Number(BigInt(x) ^ BigInt(y)); + + return (Math.floor(x / U32) ^ Math.floor(y / U32)) * U32 + ((x ^ y) >>> 0); +} + +export function bitwise_not(x) { + if (x >= MIN_I32 && x <= MAX_I32) return ~x; + if (x < MIN_SAFE || x > MAX_SAFE) return Number(~BigInt(x)); + + return ~Math.floor(x / U32) * U32 + (~x >>> 0); +} + +export function bitwise_shift_right(x, y) { + if (y === 0) return x; + if (y < 0) return bitwise_shift_left(x, -y); + if (y < 32 && x >= MIN_I32 && x <= MAX_I32) return x >> y; + if (x < MIN_SAFE || x > MAX_SAFE) return Number(BigInt(x) >> BigInt(y)); + + const ahi = Math.floor(x / U32); + + // Shifting right by y < 32 moves bits across the hi/lo boundary: + // + // before: [ hi (21 bits) | lo (32 bits) ] + // after: [ hi >> y | (hi's low y bits) ++ (lo >> y) ] + // + // The new low word has two sources: + // - lo's bits shifted down: x >>> y (>>> treats x as unsigned 32-bit) + // - hi's bottom y bits shifted up: ahi << (32 - y). + if (y < 32) return (ahi >> y) * U32 + (((x >>> y) | (ahi << (32 - y))) >>> 0); + + // Shifting by >= 32 wipes out the entire low word. The result is just the + // high word shifted right by the remaining amount. + return ahi >> (y - 32); +} + +export function bitwise_shift_left(x, y) { + if (y === 0) return x; + if (y < 0) return bitwise_shift_right(x, -y); + if (y < 31) return x * (1 << y); + return x * 2 ** y; +} + +export function inspect(v) { + return new Inspector().inspect(v); +} + +export function float_to_string(float) { + const string = float.toString().replace("+", ""); + if (string.indexOf(".") >= 0) { + return string; + } else { + const index = string.indexOf("e"); + if (index >= 0) { + return string.slice(0, index) + ".0" + string.slice(index); + } else { + return string + ".0"; + } + } +} + +class Inspector { + #references = new Set(); + + inspect(v) { + const t = typeof v; + if (v === true) return "True"; + if (v === false) return "False"; + if (v === null) return "//js(null)"; + if (v === undefined) return "Nil"; + if (t === "string") return this.#string(v); + if (t === "bigint" || Number.isInteger(v)) return v.toString(); + if (t === "number") return float_to_string(v); + if (v instanceof UtfCodepoint) return this.#utfCodepoint(v); + if (v instanceof BitArray) return this.#bit_array(v); + if (v instanceof RegExp) return `//js(${v})`; + if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`; + if (v instanceof globalThis.Error) return `//js(${v.toString()})`; + if (v instanceof Function) { + const args = []; + for (const i of Array(v.length).keys()) + args.push(String.fromCharCode(i + 97)); + return `//fn(${args.join(", ")}) { ... }`; + } + + if (this.#references.size === this.#references.add(v).size) { + return "//js(circular reference)"; + } + + let printed; + if (Array.isArray(v)) { + printed = `#(${v.map((v) => this.inspect(v)).join(", ")})`; + } else if (isList(v)) { + printed = this.#list(v); + } else if (v instanceof CustomType) { + printed = this.#customType(v); + } else if (v instanceof Dict) { + printed = this.#dict(v); + } else if (v instanceof Set) { + return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`; + } else { + printed = this.#object(v); + } + this.#references.delete(v); + return printed; + } + + #object(v) { + const name = Object.getPrototypeOf(v)?.constructor?.name || "Object"; + const props = []; + for (const k of Object.keys(v)) { + props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`); + } + const body = props.length ? " " + props.join(", ") + " " : ""; + const head = name === "Object" ? "" : name + " "; + return `//js(${head}{${body}})`; + } + + #dict(map) { + let body = "dict.from_list(["; + let first = true; + + body = dict_fold(map, body, (body, key, value) => { + if (!first) body = body + ", "; + first = false; + return body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")"; + }); + + return body + "])"; + } + + #customType(record) { + const props = Object.keys(record) + .map((label) => { + const value = this.inspect(record[label]); + return isNaN(parseInt(label)) ? `${label}: ${value}` : value; + }) + .join(", "); + return props + ? `${record.constructor.name}(${props})` + : record.constructor.name; + } + + #list(list) { + if (List$isEmpty(list)) { + return "[]"; + } + + let char_out = 'charlist.from_string("'; + let list_out = "["; + + let current = list; + while (List$isNonEmpty(current)) { + let element = current.head; + current = current.tail; + + if (list_out !== "[") { + list_out += ", "; + } + list_out += this.inspect(element); + + if (char_out) { + if (Number.isInteger(element) && element >= 32 && element <= 126) { + char_out += String.fromCharCode(element); + } else { + char_out = null; + } + } + } + + if (char_out) { + return char_out + '")'; + } else { + return list_out + "]"; + } + } + + #string(str) { + let new_str = '"'; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + switch (char) { + case "\n": + new_str += "\\n"; + break; + case "\r": + new_str += "\\r"; + break; + case "\t": + new_str += "\\t"; + break; + case "\f": + new_str += "\\f"; + break; + case "\\": + new_str += "\\\\"; + break; + case '"': + new_str += '\\"'; + break; + default: + if (char < " " || (char > "~" && char < "\u{00A0}")) { + new_str += + "\\u{" + + char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") + + "}"; + } else { + new_str += char; + } + } + } + new_str += '"'; + return new_str; + } + + #utfCodepoint(codepoint) { + return `//utfcodepoint(${String.fromCodePoint(codepoint.value)})`; + } + + #bit_array(bits) { + if (bits.bitSize === 0) { + return "<<>>"; + } + + let acc = "<<"; + + for (let i = 0; i < bits.byteSize - 1; i++) { + acc += bits.byteAt(i).toString(); + acc += ", "; + } + + if (bits.byteSize * 8 === bits.bitSize) { + acc += bits.byteAt(bits.byteSize - 1).toString(); + } else { + const trailingBitsCount = bits.bitSize % 8; + acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount); + acc += `:size(${trailingBitsCount})`; + } + + acc += ">>"; + return acc; + } +} + +export function base16_encode(bit_array) { + const trailingBitsCount = bit_array.bitSize % 8; + + let result = ""; + + for (let i = 0; i < bit_array.byteSize; i++) { + let byte = bit_array.byteAt(i); + + if (i === bit_array.byteSize - 1 && trailingBitsCount !== 0) { + const unusedBitsCount = 8 - trailingBitsCount; + byte = (byte >> unusedBitsCount) << unusedBitsCount; + } + + result += byte.toString(16).padStart(2, "0").toUpperCase(); + } + + return result; +} + +export function base16_decode(string) { + const bytes = new Uint8Array(string.length / 2); + for (let i = 0; i < string.length; i += 2) { + const a = parseInt(string[i], 16); + const b = parseInt(string[i + 1], 16); + if (isNaN(a) || isNaN(b)) return Result$Error(Nil); + bytes[i / 2] = a * 16 + b; + } + return Result$Ok(new BitArray(bytes)); +} + +export function bit_array_to_int_and_size(bits) { + const trailingBitsCount = bits.bitSize % 8; + const unusedBitsCount = trailingBitsCount === 0 ? 0 : 8 - trailingBitsCount; + + return [bits.byteAt(0) >> unusedBitsCount, bits.bitSize]; +} + +export function bit_array_starts_with(bits, prefix) { + if (prefix.bitSize > bits.bitSize) { + return false; + } + + // Check any whole bytes + const byteCount = Math.trunc(prefix.bitSize / 8); + for (let i = 0; i < byteCount; i++) { + if (bits.byteAt(i) !== prefix.byteAt(i)) { + return false; + } + } + + // Check any trailing bits at the end of the prefix + if (prefix.bitSize % 8 !== 0) { + const unusedBitsCount = 8 - (prefix.bitSize % 8); + if ( + bits.byteAt(byteCount) >> unusedBitsCount !== + prefix.byteAt(byteCount) >> unusedBitsCount + ) { + return false; + } + } + + return true; +} + +export function log(x) { + // It is checked in Gleam that: + // - The input is strictly positive (x > 0) + // - This ensures that Math.log will never return NaN or -Infinity + // The function can thus safely pass the input to Math.log + // and a valid finite float will always be produced. + return Math.log(x); +} + +export function exp(x) { + return Math.exp(x); +} + +export function list_to_array(list) { + let current = list; + let array = []; + while (List$isNonEmpty(current)) { + array.push(current.head); + current = current.tail; + } + return array; +} + +export function index(data, key) { + // Dictionaries and dictionary-like objects can be indexed + if (data instanceof Dict) { + const result = dict_get(data, key); + return Result$Ok(result.isOk() ? new Some(result[0]) : new None()); + } + + if (data instanceof WeakMap || data instanceof Map) { + const token = {}; + const entry = data.get(key, token); + if (entry === token) return Result$Ok(new None()); + return Result$Ok(new Some(entry)); + } + + const key_is_int = Number.isInteger(key); + + // Only elements 0-7 of lists can be indexed, negative indices are not allowed + if (key_is_int && key >= 0 && key < 8 && isList(data)) { + let i = 0; + for (const value of data) { + if (i === key) return Result$Ok(new Some(value)); + i++; + } + return Result$Error("Indexable"); + } + + // Arrays and objects can be indexed + if ( + (key_is_int && Array.isArray(data)) || + (data && typeof data === "object") || + (data && Object.getPrototypeOf(data) === Object.prototype) + ) { + if (key in data) return Result$Ok(new Some(data[key])); + return Result$Ok(new None()); + } + + return Result$Error(key_is_int ? "Indexable" : "Dict"); +} + +export function list(data, decode, pushPath, index, emptyList) { + if (!(isList(data) || Array.isArray(data))) { + const error = DecodeError$DecodeError("List", classify(data), emptyList); + return [emptyList, arrayToList([error])]; + } + + const decoded = []; + + for (const element of data) { + const layer = decode(element); + const [out, errors] = layer; + + if (List$isNonEmpty(errors)) { + const [_, errors] = pushPath(layer, index.toString()); + return [emptyList, errors]; + } + decoded.push(out); + index++; + } + + return [arrayToList(decoded), emptyList]; +} + +export function dict(data) { + if (data instanceof Dict) { + return Result$Ok(data); + } + if (data instanceof Map || data instanceof WeakMap) { + return Result$Ok(dict_from_iterable(data)); + } + if (data == null) { + return Result$Error("Dict"); + } + if (typeof data !== "object") { + return Result$Error("Dict"); + } + const proto = Object.getPrototypeOf(data); + if (proto === Object.prototype || proto === null) { + return Result$Ok(dict_from_iterable(Object.entries(data))); + } + return Result$Error("Dict"); +} + +export function bit_array(data) { + if (data instanceof BitArray) return Result$Ok(data); + if (data instanceof Uint8Array) return Result$Ok(new BitArray(data)); + return Result$Error(new BitArray(new Uint8Array())); +} + +export function float(data) { + if (typeof data === "number") return Result$Ok(data); + return Result$Error(0.0); +} + +export function int(data) { + if (Number.isInteger(data)) return Result$Ok(data); + return Result$Error(0); +} + +export function string(data) { + if (typeof data === "string") return Result$Ok(data); + return Result$Error(""); +} + +export function is_null(data) { + return data === null || data === undefined; +} + +function arrayToList(array) { + let list = List$Empty(); + let i = array.length; + while (i--) { + list = List$NonEmpty(array[i], list); + } + return list; +} + +function isList(data) { + return List$isEmpty(data) || List$isNonEmpty(data); +} + +function isResult(data) { + return Result$isOk(data) || Result$isError(data); +} + +export function string_remove_prefix(string, prefix) { + if (string.startsWith(prefix)) { + return string.slice(prefix.length); + } + return string; +} + +export function string_remove_suffix(string, suffix) { + if (string.endsWith(suffix)) { + return string.slice(0, string.length - suffix.length); + } + return string; +} diff --git a/ui/javascript/gleeunit/_gleam_artefacts/gleeunit.cache b/ui/javascript/gleeunit/_gleam_artefacts/gleeunit.cache new file mode 100644 index 0000000000000000000000000000000000000000..3f2b8f14cbabc7421994133f734119a72bc7a0e2 GIT binary patch literal 9318 zcmds7?Ta1N9iE%48n;?hsA-78G^Dno-m4&#rY z+bH5oDr$_>Dg}j95&d8ZiekhFf>NYfu!ZV>V6iG_rA48Iw$Ja(dFIZXJ9BqFq+fd2 z=gyonzxVSyFLP$sSXr6(U3VCIsnpgOZKvt_l)i3Cqo9TR$MN78VA5+cbrCxAxmhpD zE%I8{`MSDQTX$0&&Btz%>=>k86pHLyPq(a}0jA8NoF+C*)>M>cS?B2LPInN+=})5m z(08{xfh&4Cux0Zpu%}K(*^;m6>Wp;VC@4o^z_C+cM1}aWenx#J>_vSqoRbd#ZSF6XL7NhUJ|U6szIg+eOr%wx{OW}O{j z&N;$gS!hrxe%rhxw?ZtSMNKSd*3-(Cv$aLLdXDez_5wE=rU`YK)|L!q-d{gTU7`QF zX5y%ihhbMOR}?DFj6EC+DuaGCdU0O$WD6l^ao{la-MKk$T$q?3>L^6&#ZvNv=~4{1 z5H|@ld5hz`s3^NXW$Gp*{t8-0*ihQjj_&j)2Qp{U5F41dvmVZy!b0@{kHN-_xe+8_CY$xj)M5vzsY}d|A{|wFfckNm{}M1RRdY4*VM4mK zS{A2QV{s5`#vk1YTOO>iM_D63+AQ3a3mq%Xjpo|YXskAy6k!J=zu@?#8MxR!7FgQ< zGW>r_4V{X-EJ+YQ8+)!Kg29+_Y)ADa2s#0nxkHvl(I&H* z{}96L(CaeAiY3M)v^;~PdA=fXXmcZDCMEJnX+P=)j;CH#pjb^*v`0T03yIK4`4CQg zr5tIYSgrH2W~<%d@Ro(D9UM$(w`h{7klt&C(q+XC}iB znu$FZ#df;j+Pz^MyJ2b%;^?5;OYLET<;b2W3v4e``VWX*2kgEVLwgi2+e==$ z0O28Y*={H=VIZBh4|Fp^FR`(7CSKpgUK?~Jx0J(69=x4(#c^dsMv^W#aMB6AUSgXv z<{Zx-#xAHWm6cU&5`2qmLRmoBunqEQ6XB7iyO2S)oMdBfu&eFunl?D`L<=0YtIHrob+2TCY z#DZokWv0K0yh(9iWv$naVBkA}?A5Y8TsKgMHC-7kqC|=~(tvy{5y<}s$ut$A@?hX| zrKIpviY&9BVnbM9$#-JPof@8L%`k_VtUKrVZvMt49#%Y+W`PuDb>O7E1YJcqoY0l-_t0) zG=o!0cnN`&;=0Fi&OJ5-HR(Q+H>%6j^*HRdn zX{9&Fn@NhR?A$K`t^s}s907g@{2o{f6+3~wz%KyVnm!F22VMYP1msQNpTJp|F4qAY zfxSQg+y=zJ-M~rUBVZ#s_D8^vf&G9790KkFjsQo2M*w-Rd>4=lH7f%Df1zrX|2d1#gI~_O!kn_8rYaoQG?AF%~_OYG7?R!q=ptIbW zb$W}#LDEU$UMF>fK__tH#eTFDUfG!)dVaq%0D&{_CizR1BIWYcRx7R+JvtG&ynRLc z^1PKdh@vW7?-Xyx$C+zkRdj+5zF^^#L%A(G*ZQWGQ1y%_lGx-YUD@>`AyhdJy|EsU z!VyUOEBDRF5LIOJ{BJ<)-jBw4K!jFlN6)E}416y?z&HV|uWQX^rp!giR%IA2#m<2C zeh7|FYV2j`Mw=KKy8{;N1H{8`$jlQk zVZIQJ8)}%fcb1&?koN?bP6EPvH+~KS5)vv>*}_Wc^HBiGSW}zsVic&N4-=^DBS`)X z0GIU)uFnJb%eR4})D#LSfiocU+qij0SN@Lcm-@a=w?3@XRF)je-KW!PGa7FMM0Ytu zvV^Dku{-dco=ao)Ab_{%ARfZ?9(~_JeAeg~nbv3$I5IkJ`6cu|4v1WaR=G}M`7lJg zuSDb3fY`bXjW=n#O+^h}7AW#PDE=4_{qLgjFIr^D77~xXEB+Mp2LQ3e)Xr%D(_Hom zgzP^+EUyJb*CqJ5szv}^mjgTU@^nskFT~HcYIxaQ`jn0AJWmt$bI`aJ5ZgJLN7+W@ zzBBp|^+z=RSwAqn-2f?4R{j(~TRuf>&(_)Z6&lxRS4tsgMSTJkOQk#t8-5K`MV&KQ z_O?!4^2+|Y*7aR9wgKVhHrDipb?&Vw=ULbzpUfqG7eL-dK;+QOQp(CMgC-va;R98~ zXj3J7uVn3SQ1GRW$G_yNy$zr!0YvN_xZb1hkKp>KzCRa2)=pBDeMqNei%6B*fH613 zM=3Z%)_rb02_khGhvt9aUJgBl?`_b&qs@&WYdA?B_OJ}+8{m0U^Zf@VuhM)BKfz2CnY`VwWjr@?MkDX2C80sUp%o!1dD_X-uBN|8!8qufQVz z*C8VI!zs7MF$PxNJB3!t6X-|zXC0|u3>lYc^*2Js4lM_dRVn409Qsh;{h)XVkSJ^c W(+!$&53c+4{huKCeJxzev;GShJQzg) literal 0 HcmV?d00001 diff --git a/ui/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_meta b/ui/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..5c75cc992e83eca70e609b683e4827f800e34f78 GIT binary patch literal 479 zcmZY5F-QV&6vy#rULJ@`K}6_ijoK7spg2fNQ-gxk;2?5z2Hl+wIYf(t>A74}gSogw zC`d$ZLPO*xZLLvlQDZ~&y|{+x!H56j_xQv6|DCj|iB~?q*95(@q!$b2xUQL&8_Xf} zXJQo1if+x5E&rRDmZ9yJR>;?}`dt0G`gok?=zq(-=gt$@KohsP!=g`Q8(GxRKo^(j zV}K`oA~YkigebBo;{XPZF~SR?JTHk9^3YJj5t^{k#t`?IKv6_uNFakfv|!^5J&f^y zcPz}Z3&MzC3l2`v!8r!F#tlYzMJyl^$0qXF!7gezK?^pzxWEu^P*jlsRIK69!x#S4 EH!O@lYybcN literal 0 HcmV?d00001 diff --git a/ui/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache b/ui/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache new file mode 100644 index 0000000000000000000000000000000000000000..d41b76ed45a83e9c1042ec23a57995df05d43b81 GIT binary patch literal 15775 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 Nf-g9gXKOx<{{SQ~!TtaM literal 0 HcmV?d00001 diff --git a/ui/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache b/ui/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache new file mode 100644 index 0000000000000000000000000000000000000000..a759bef629f3374a8a65e4bcc5cbef91f2dbd7bb GIT binary patch literal 4261 zcmchaU1%It6vroOYn4jXCq;!-{Cbc~Q?V)ve#A7i+Lov_+8UF|?qsuOcV^w0rKAtV zh*W)3kg8Zg(Wlrz3PlU8A0QQkzWCsSD1}OV5ep&#D@gTs?%b2zyV+?Xf|vd8J@=mP zbMMT(v%$++xf+JesFM20WZbNlWO7{x%MQIuEOD{zcT}p%>VXhQ>%#WdXDn}5I?*4O z!g^eZQaiPzCu4Q-a2z|fFS5eQf3ndGRo`1Kvzn@bM{FbmwwIul zr~S?;O+{t|ss*Xe1{#gH(J7}^k-H2?(_)T?qoJr|vzggtybYGyu_)^1XJWbBl(&bL z*$|%c)wcE;E9Qb)_j+iC^R^y3K4QlveVNn=wHdz9465o#1YAZ=CL$qw=4@3Oc)rZKt9Bw~UXf87+ZM z=o1yi>FBcEx15=ilvtJa9F?9i)pnOr`Re^+8&2mqv(oxsaXnOk9T-oko>+AmIbrL* z*oH<`v^+O#<*Q-s)%Yr20f##>o$Fi(70{RPM9?|_BqV1(Cl8Hl3xVx6!MS-Tzq5Dp z==OlOy-O$B&jAT+880U|En*MeZs`)7@-G;iR-yd}>r_};^VPhs(zdfsvT1u?8P*EP zd`>`TOi*s6|HouB{jEySZw7aR5KIF(5e|XF-~>1c7QiC7idcQ{7#Ie7zzC3M_+{`W z_z28_Q{YGNC%BGs-2?i;AlM4z0gAys@CG;yegX?X&etp0T-Sh2;3aSXyb9zcI0ue_ zZ$W{Q-VAO9awKdAPlHMDDL4*315*A4a2A{gvh=dQZU=p!9}EC_AVxq0>J~_ww#W8< zqZ#?Nuv7{B=Ywf~JSa{z>xrKV7R~OqJrj(I$VqS*e!%Is!pCOnkPPz2qU< z8kiejui1mvL+fp{vA3^xqs~%G)B0>**p-BZbTTZA1xXS%(y_u+IK4M+lnRw7NyDI2 zh$rAzl0q#mHLGFo(j80RDaNUvq>V~ccIUEF@oMWSXLGkb19F0Ce6O=GZvlQ@j;1Vu zG`lIL-t^NrD z{1S-&oR!q#b5W7=MXj8~>nR`^j*|X_T|cK1IxwGNs(C-N4_WClebv%A{^6QtE=m%o zv>}W=1;l%fUR&I9#aGQk<3?#9~~5HsiK&s)0NR#6Mthl4nhHUx$Nir%|SegMSUy+rW<5ZVbu71_LM z@gOqtbtba=`ICFH(1Ym9r;pI5>E&7>^i3F)`;ySZ^m4Hin$9%mT`Kn?2~OH~82H{A OxfbagtYXIXyng^UFaG)f literal 0 HcmV?d00001 diff --git a/ui/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache_meta b/ui/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..1cab53c57708d8eca2a02479da2199d2c06de569 GIT binary patch literal 397 zcmY+;Jt)Ls90%~{ylg00$YQv#T@22Cq0^Zuol?#@ad)_Z8>Nd;8I49am?$rsEEc;A z`WtN)1Nof(2H*PheEU7Wr=G^wz(`GCDzuT#8m&(P0 z`7T=*t9|L7~f%Y@Q;+uGTq&MD{Ia8JYsy@?P~= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type atom_() :: any(). + +-type encoding() :: utf8. + +-type report_module_name() :: gleeunit_progress. + +-type gleeunit_progress_option() :: {colored, boolean()}. + +-type eunit_option() :: verbose | + no_tty | + {report, {report_module_name(), list(gleeunit_progress_option())}} | + {scale_timeouts, integer()}. + +-file("src/gleeunit.gleam", 42). +-spec gleam_to_erlang_module_name(binary()) -> binary(). +gleam_to_erlang_module_name(Path) -> + case gleam_stdlib:string_ends_with(Path, <<".gleam"/utf8>>) of + true -> + _pipe = Path, + _pipe@1 = gleam@string:replace( + _pipe, + <<".gleam"/utf8>>, + <<""/utf8>> + ), + gleam@string:replace(_pipe@1, <<"/"/utf8>>, <<"@"/utf8>>); + + false -> + _pipe@2 = Path, + _pipe@3 = gleam@string:split(_pipe@2, <<"/"/utf8>>), + _pipe@4 = gleam@list:last(_pipe@3), + _pipe@5 = gleam@result:unwrap(_pipe@4, Path), + gleam@string:replace(_pipe@5, <<".erl"/utf8>>, <<""/utf8>>) + end. + +-file("src/gleeunit.gleam", 18). +-spec do_main() -> nil. +do_main() -> + Options = [verbose, + no_tty, + {report, {gleeunit_progress, [{colored, true}]}}, + {scale_timeouts, 10}], + Result = begin + _pipe = gleeunit_ffi:find_files( + <<"**/*.{erl,gleam}"/utf8>>, + <<"test"/utf8>> + ), + _pipe@1 = gleam@list:map(_pipe, fun gleam_to_erlang_module_name/1), + _pipe@2 = gleam@list:map( + _pipe@1, + fun(_capture) -> erlang:binary_to_atom(_capture, utf8) end + ), + gleeunit_ffi:run_eunit(_pipe@2, Options) + end, + Code = case Result of + {ok, _} -> + 0; + + {error, _} -> + 1 + end, + erlang:halt(Code). + +-file("src/gleeunit.gleam", 13). +?DOC( + " Find and run all test functions for the current project using Erlang's EUnit\n" + " test framework, or a custom JavaScript test runner.\n" + "\n" + " Any Erlang or Gleam function in the `test` directory with a name ending in\n" + " `_test` is considered a test function and will be run.\n" + "\n" + " A test that panics is considered a failure.\n" +). +-spec main() -> nil. +main() -> + do_main(). diff --git a/ui/javascript/gleeunit/gleeunit.mjs b/ui/javascript/gleeunit/gleeunit.mjs new file mode 100644 index 0000000..83018dd --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit.mjs @@ -0,0 +1,62 @@ +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $result from "../gleam_stdlib/gleam/result.mjs"; +import * as $string from "../gleam_stdlib/gleam/string.mjs"; +import { CustomType as $CustomType } from "./gleam.mjs"; +import { main as do_main } from "./gleeunit_ffi.mjs"; + +class Utf8 extends $CustomType {} + +class GleeunitProgress extends $CustomType {} + +class Colored extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +class Verbose extends $CustomType {} + +class NoTty extends $CustomType {} + +class Report extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +class ScaleTimeouts extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +function gleam_to_erlang_module_name(path) { + let $ = $string.ends_with(path, ".gleam"); + if ($) { + let _pipe = path; + let _pipe$1 = $string.replace(_pipe, ".gleam", ""); + return $string.replace(_pipe$1, "/", "@"); + } else { + let _pipe = path; + let _pipe$1 = $string.split(_pipe, "/"); + let _pipe$2 = $list.last(_pipe$1); + let _pipe$3 = $result.unwrap(_pipe$2, path); + return $string.replace(_pipe$3, ".erl", ""); + } +} + +/** + * Find and run all test functions for the current project using Erlang's EUnit + * test framework, or a custom JavaScript test runner. + * + * Any Erlang or Gleam function in the `test` directory with a name ending in + * `_test` is considered a test function and will be run. + * + * A test that panics is considered a failure. + */ +export function main() { + return do_main(); +} diff --git a/ui/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs b/ui/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs new file mode 100644 index 0000000..f6af89d --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs @@ -0,0 +1,180 @@ +import * as $dynamic from "../../../gleam_stdlib/gleam/dynamic.mjs"; +import { CustomType as $CustomType } from "../../gleam.mjs"; +import { from_dynamic } from "./gleeunit_gleam_panic_ffi.mjs"; + +export { from_dynamic }; + +export class GleamPanic extends $CustomType { + constructor(message, file, module, function$, line, kind) { + super(); + this.message = message; + this.file = file; + this.module = module; + this.function = function$; + this.line = line; + this.kind = kind; + } +} +export const GleamPanic$GleamPanic = (message, file, module, function$, line, kind) => + new GleamPanic(message, file, module, function$, line, kind); +export const GleamPanic$isGleamPanic = (value) => value instanceof GleamPanic; +export const GleamPanic$GleamPanic$message = (value) => value.message; +export const GleamPanic$GleamPanic$0 = (value) => value.message; +export const GleamPanic$GleamPanic$file = (value) => value.file; +export const GleamPanic$GleamPanic$1 = (value) => value.file; +export const GleamPanic$GleamPanic$module = (value) => value.module; +export const GleamPanic$GleamPanic$2 = (value) => value.module; +export const GleamPanic$GleamPanic$function = (value) => value.function; +export const GleamPanic$GleamPanic$3 = (value) => value.function; +export const GleamPanic$GleamPanic$line = (value) => value.line; +export const GleamPanic$GleamPanic$4 = (value) => value.line; +export const GleamPanic$GleamPanic$kind = (value) => value.kind; +export const GleamPanic$GleamPanic$5 = (value) => value.kind; + +export class Todo extends $CustomType {} +export const PanicKind$Todo = () => new Todo(); +export const PanicKind$isTodo = (value) => value instanceof Todo; + +export class Panic extends $CustomType {} +export const PanicKind$Panic = () => new Panic(); +export const PanicKind$isPanic = (value) => value instanceof Panic; + +export class LetAssert extends $CustomType { + constructor(start, end, pattern_start, pattern_end, value) { + super(); + this.start = start; + this.end = end; + this.pattern_start = pattern_start; + this.pattern_end = pattern_end; + this.value = value; + } +} +export const PanicKind$LetAssert = (start, end, pattern_start, pattern_end, value) => + new LetAssert(start, end, pattern_start, pattern_end, value); +export const PanicKind$isLetAssert = (value) => value instanceof LetAssert; +export const PanicKind$LetAssert$start = (value) => value.start; +export const PanicKind$LetAssert$0 = (value) => value.start; +export const PanicKind$LetAssert$end = (value) => value.end; +export const PanicKind$LetAssert$1 = (value) => value.end; +export const PanicKind$LetAssert$pattern_start = (value) => value.pattern_start; +export const PanicKind$LetAssert$2 = (value) => value.pattern_start; +export const PanicKind$LetAssert$pattern_end = (value) => value.pattern_end; +export const PanicKind$LetAssert$3 = (value) => value.pattern_end; +export const PanicKind$LetAssert$value = (value) => value.value; +export const PanicKind$LetAssert$4 = (value) => value.value; + +export class Assert extends $CustomType { + constructor(start, end, expression_start, kind) { + super(); + this.start = start; + this.end = end; + this.expression_start = expression_start; + this.kind = kind; + } +} +export const PanicKind$Assert = (start, end, expression_start, kind) => + new Assert(start, end, expression_start, kind); +export const PanicKind$isAssert = (value) => value instanceof Assert; +export const PanicKind$Assert$start = (value) => value.start; +export const PanicKind$Assert$0 = (value) => value.start; +export const PanicKind$Assert$end = (value) => value.end; +export const PanicKind$Assert$1 = (value) => value.end; +export const PanicKind$Assert$expression_start = (value) => + value.expression_start; +export const PanicKind$Assert$2 = (value) => value.expression_start; +export const PanicKind$Assert$kind = (value) => value.kind; +export const PanicKind$Assert$3 = (value) => value.kind; + +export class BinaryOperator extends $CustomType { + constructor(operator, left, right) { + super(); + this.operator = operator; + this.left = left; + this.right = right; + } +} +export const AssertKind$BinaryOperator = (operator, left, right) => + new BinaryOperator(operator, left, right); +export const AssertKind$isBinaryOperator = (value) => + value instanceof BinaryOperator; +export const AssertKind$BinaryOperator$operator = (value) => value.operator; +export const AssertKind$BinaryOperator$0 = (value) => value.operator; +export const AssertKind$BinaryOperator$left = (value) => value.left; +export const AssertKind$BinaryOperator$1 = (value) => value.left; +export const AssertKind$BinaryOperator$right = (value) => value.right; +export const AssertKind$BinaryOperator$2 = (value) => value.right; + +export class FunctionCall extends $CustomType { + constructor(arguments$) { + super(); + this.arguments = arguments$; + } +} +export const AssertKind$FunctionCall = (arguments$) => + new FunctionCall(arguments$); +export const AssertKind$isFunctionCall = (value) => + value instanceof FunctionCall; +export const AssertKind$FunctionCall$arguments = (value) => value.arguments; +export const AssertKind$FunctionCall$0 = (value) => value.arguments; + +export class OtherExpression extends $CustomType { + constructor(expression) { + super(); + this.expression = expression; + } +} +export const AssertKind$OtherExpression = (expression) => + new OtherExpression(expression); +export const AssertKind$isOtherExpression = (value) => + value instanceof OtherExpression; +export const AssertKind$OtherExpression$expression = (value) => + value.expression; +export const AssertKind$OtherExpression$0 = (value) => value.expression; + +export class AssertedExpression extends $CustomType { + constructor(start, end, kind) { + super(); + this.start = start; + this.end = end; + this.kind = kind; + } +} +export const AssertedExpression$AssertedExpression = (start, end, kind) => + new AssertedExpression(start, end, kind); +export const AssertedExpression$isAssertedExpression = (value) => + value instanceof AssertedExpression; +export const AssertedExpression$AssertedExpression$start = (value) => + value.start; +export const AssertedExpression$AssertedExpression$0 = (value) => value.start; +export const AssertedExpression$AssertedExpression$end = (value) => value.end; +export const AssertedExpression$AssertedExpression$1 = (value) => value.end; +export const AssertedExpression$AssertedExpression$kind = (value) => value.kind; +export const AssertedExpression$AssertedExpression$2 = (value) => value.kind; + +export class Literal extends $CustomType { + constructor(value) { + super(); + this.value = value; + } +} +export const ExpressionKind$Literal = (value) => new Literal(value); +export const ExpressionKind$isLiteral = (value) => value instanceof Literal; +export const ExpressionKind$Literal$value = (value) => value.value; +export const ExpressionKind$Literal$0 = (value) => value.value; + +export class Expression extends $CustomType { + constructor(value) { + super(); + this.value = value; + } +} +export const ExpressionKind$Expression = (value) => new Expression(value); +export const ExpressionKind$isExpression = (value) => + value instanceof Expression; +export const ExpressionKind$Expression$value = (value) => value.value; +export const ExpressionKind$Expression$0 = (value) => value.value; + +export class Unevaluated extends $CustomType {} +export const ExpressionKind$Unevaluated = () => new Unevaluated(); +export const ExpressionKind$isUnevaluated = (value) => + value instanceof Unevaluated; diff --git a/ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl b/ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl new file mode 100644 index 0000000..d78f5e5 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl @@ -0,0 +1,49 @@ +-module(gleeunit_gleam_panic_ffi). +-export([from_dynamic/1]). + +from_dynamic(#{ + gleam_error := assert, + start := Start, + 'end' := End, + expression_start := EStart +} = E) -> + wrap(E, {assert, Start, End, EStart, assert_kind(E)}); +from_dynamic(#{ + gleam_error := let_assert, + start := Start, + 'end' := End, + pattern_start := PStart, + pattern_end := PEnd, + value := Value +} = E) -> + wrap(E, {let_assert, Start, End, PStart, PEnd, Value}); +from_dynamic(#{gleam_error := panic} = E) -> + wrap(E, panic); +from_dynamic(#{gleam_error := todo} = E) -> + wrap(E, todo); +from_dynamic(_) -> + {error, nil}. + +assert_kind(#{kind := binary_operator, left := L, right := R, operator := O}) -> + {binary_operator, atom_to_binary(O), expression(L), expression(R)}; +assert_kind(#{kind := function_call, arguments := Arguments}) -> + {function_call, lists:map(fun expression/1, Arguments)}; +assert_kind(#{kind := expression, expression := Expression}) -> + {other_expression, expression(Expression)}. + +expression(#{start := S, 'end' := E, kind := literal, value := Value}) -> + {asserted_expression, S, E, {literal, Value}}; +expression(#{start := S, 'end' := E, kind := expression, value := Value}) -> + {asserted_expression, S, E, {expression, Value}}; +expression(#{start := S, 'end' := E, kind := unevaluated}) -> + {asserted_expression, S, E, unevaluated}. + +wrap(#{ + gleam_error := _, + file := File, + message := Message, + module := Module, + function := Function, + line := Line +}, Kind) -> + {ok, {gleam_panic, Message, File, Module, Function, Line, Kind}}. diff --git a/ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs b/ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs new file mode 100644 index 0000000..03f6025 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs @@ -0,0 +1,91 @@ +import { Result$Ok, Result$Error, List$Empty, List$NonEmpty } from "../../gleam.mjs"; +import { + GleamPanic$GleamPanic, + PanicKind$Todo, + PanicKind$Panic, + PanicKind$LetAssert, + PanicKind$Assert, + AssertKind$BinaryOperator, + AssertKind$FunctionCall, + AssertKind$OtherExpression, + AssertedExpression$AssertedExpression, + ExpressionKind$Literal, + ExpressionKind$Expression, + ExpressionKind$Unevaluated, +} from "./gleam_panic.mjs"; + +export function from_dynamic(error) { + if (!(error instanceof globalThis.Error) || !error.gleam_error) { + return Result$Error(undefined); + } + + if (error.gleam_error === "todo") { + return wrap(error, PanicKind$Todo()); + } + + if (error.gleam_error === "panic") { + return wrap(error, PanicKind$Panic()); + } + + if (error.gleam_error === "let_assert") { + let kind = PanicKind$LetAssert( + error.start, + error.end, + error.pattern_start, + error.pattern_end, + error.value, + ); + return wrap(error, kind); + } + + if (error.gleam_error === "assert") { + let kind = PanicKind$Assert( + error.start, + error.end, + error.expression_start, + assert_kind(error), + ); + return wrap(error, kind); + } + + return Result$Error(undefined); +} + +function assert_kind(error) { + if (error.kind == "binary_operator") { + return AssertKind$BinaryOperator( + error.operator, + expression(error.left), + expression(error.right), + ); + } + + if (error.kind == "function_call") { + let list = List$Empty(); + let i = error.arguments.length; + while (i--) { + list = List$NonEmpty(expression(error.arguments[i]), list); + } + return AssertKind$FunctionCall(list); + } + + return AssertKind$OtherExpression(expression(error.expression)); +} + +function expression(data) { + const expression = AssertedExpression$AssertedExpression(data.start, data.end, undefined); + if (data.kind == "literal") { + expression.kind = ExpressionKind$Literal(data.value); + } else if (data.kind == "expression") { + expression.kind = ExpressionKind$Expression(data.value); + } else { + expression.kind = ExpressionKind$Unevaluated(); + } + return expression; +} + +function wrap(e, kind) { + return Result$Ok( + GleamPanic$GleamPanic(e.message, e.file, e.module, e.function, e.line, kind), + ); +} diff --git a/ui/javascript/gleeunit/gleeunit/internal/reporting.mjs b/ui/javascript/gleeunit/gleeunit/internal/reporting.mjs new file mode 100644 index 0000000..636c3e2 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit/internal/reporting.mjs @@ -0,0 +1,257 @@ +import * as $bit_array from "../../../gleam_stdlib/gleam/bit_array.mjs"; +import * as $dynamic from "../../../gleam_stdlib/gleam/dynamic.mjs"; +import * as $int from "../../../gleam_stdlib/gleam/int.mjs"; +import * as $io from "../../../gleam_stdlib/gleam/io.mjs"; +import * as $list from "../../../gleam_stdlib/gleam/list.mjs"; +import * as $option 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 { Ok, Error, toList, CustomType as $CustomType } from "../../gleam.mjs"; +import * as $gleam_panic from "../../gleeunit/internal/gleam_panic.mjs"; +import { read_file as read_file_text } from "../../gleeunit_ffi.mjs"; + +export class State extends $CustomType { + constructor(passed, failed, skipped) { + super(); + this.passed = passed; + this.failed = failed; + this.skipped = skipped; + } +} +export const State$State = (passed, failed, skipped) => + new State(passed, failed, skipped); +export const State$isState = (value) => value instanceof State; +export const State$State$passed = (value) => value.passed; +export const State$State$0 = (value) => value.passed; +export const State$State$failed = (value) => value.failed; +export const State$State$1 = (value) => value.failed; +export const State$State$skipped = (value) => value.skipped; +export const State$State$2 = (value) => value.skipped; + +export function new_state() { + return new State(0, 0, 0); +} + +function red(text) { + return ("\u{001b}[31m" + text) + "\u{001b}[39m"; +} + +function yellow(text) { + return ("\u{001b}[33m" + text) + "\u{001b}[39m"; +} + +function green(text) { + return ("\u{001b}[32m" + text) + "\u{001b}[39m"; +} + +export function finished(state) { + let $ = state.failed; + if ($ === 0) { + let $1 = state.skipped; + if ($1 === 0) { + let $2 = state.passed; + if ($2 === 0) { + $io.println("\nNo tests found!"); + return 1; + } else { + let message = ("\n" + $int.to_string(state.passed)) + " passed, no failures"; + $io.println(green(message)); + return 0; + } + } else { + let message = ((("\n" + $int.to_string(state.passed)) + " passed, 0 failures, ") + $int.to_string( + state.skipped, + )) + " skipped"; + $io.println(yellow(message)); + return 1; + } + } else { + let $1 = state.skipped; + if ($1 === 0) { + let message = ((("\n" + $int.to_string(state.passed)) + " passed, ") + $int.to_string( + state.failed, + )) + " failures"; + $io.println(red(message)); + return 1; + } else { + let message = ((((("\n" + $int.to_string(state.passed)) + " passed, ") + $int.to_string( + state.failed, + )) + " failures, ") + $int.to_string(state.skipped)) + " skipped"; + $io.println(red(message)); + return 1; + } + } +} + +export function test_passed(state) { + $io.print(green(".")); + return new State(state.passed + 1, state.failed, state.skipped); +} + +function grey(text) { + return ("\u{001b}[90m" + text) + "\u{001b}[39m"; +} + +function format_unknown(module, function$, error) { + return $string.concat( + toList([ + grey((module + ".") + function$) + "\n", + "An unexpected error occurred:\n", + "\n", + (" " + $string.inspect(error)) + "\n", + ]), + ); +} + +function cyan(text) { + return ("\u{001b}[36m" + text) + "\u{001b}[39m"; +} + +function code_snippet(src, start, end) { + let _pipe = $result.try$( + $option.to_result(src, undefined), + (src) => { + return $result.try$( + $bit_array.slice(src, start, end - start), + (snippet) => { + return $result.try$( + $bit_array.to_string(snippet), + (snippet) => { + let snippet$1 = ((cyan(" code") + ": ") + snippet) + "\n"; + return new Ok(snippet$1); + }, + ); + }, + ); + }, + ); + return $result.unwrap(_pipe, ""); +} + +function bold(text) { + return ("\u{001b}[1m" + text) + "\u{001b}[22m"; +} + +function inspect_value(value) { + let $ = value.kind; + if ($ instanceof $gleam_panic.Literal) { + let value$1 = $.value; + return $string.inspect(value$1); + } else if ($ instanceof $gleam_panic.Expression) { + let value$1 = $.value; + return $string.inspect(value$1); + } else { + return grey("unevaluated"); + } +} + +function assert_value(name, value) { + return ((cyan(name) + ": ") + inspect_value(value)) + "\n"; +} + +function assert_info(kind) { + if (kind instanceof $gleam_panic.BinaryOperator) { + let left = kind.left; + let right = kind.right; + return $string.concat( + toList([assert_value(" left", left), assert_value("right", right)]), + ); + } else if (kind instanceof $gleam_panic.FunctionCall) { + let arguments$ = kind.arguments; + let _pipe = arguments$; + let _pipe$1 = $list.index_map( + _pipe, + (e, i) => { + let number = $string.pad_start($int.to_string(i), 5, " "); + return assert_value(number, e); + }, + ); + return $string.concat(_pipe$1); + } else { + return ""; + } +} + +function format_gleam_error(error, module, function$, src) { + let location = grey((error.file + ":") + $int.to_string(error.line)); + let $ = error.kind; + if ($ instanceof $gleam_panic.Todo) { + return $string.concat( + toList([ + ((bold(yellow("todo")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } else if ($ instanceof $gleam_panic.Panic) { + return $string.concat( + toList([ + ((bold(red("panic")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } else if ($ instanceof $gleam_panic.LetAssert) { + let start = $.start; + let end = $.end; + let value = $.value; + return $string.concat( + toList([ + ((bold(red("let assert")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + code_snippet(src, start, end), + ((cyan("value") + ": ") + $string.inspect(value)) + "\n", + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } else { + let start = $.start; + let end = $.end; + let kind = $.kind; + return $string.concat( + toList([ + ((bold(red("assert")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + code_snippet(src, start, end), + assert_info(kind), + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } +} + +function read_file(path) { + let $ = read_file_text(path); + if ($ instanceof Ok) { + let text = $[0]; + return new Ok($bit_array.from_string(text)); + } else { + return $; + } +} + +export function test_failed(state, module, function$, error) { + let _block; + let $ = $gleam_panic.from_dynamic(error); + if ($ instanceof Ok) { + let error$1 = $[0]; + let src = $option.from_result(read_file(error$1.file)); + _block = format_gleam_error(error$1, module, function$, src); + } else { + _block = format_unknown(module, function$, error); + } + let message = _block; + $io.print("\n" + message); + return new State(state.passed, state.failed + 1, state.skipped); +} + +export function eunit_missing() { + let message = bold(red("Error")) + ": EUnit libraries not found.\n\nYour Erlang installation seems to be incomplete. If you installed Erlang using\na package manager ensure that you have installed the full Erlang\ndistribution instead of a stripped-down version.\n"; + $io.print_error(message); + return new Error(undefined); +} + +export function test_skipped(state, module, function$) { + $io.print(((("\n" + module) + ".") + function$) + yellow(" skipped")); + return new State(state.passed, state.failed, state.skipped + 1); +} diff --git a/ui/javascript/gleeunit/gleeunit/should.mjs b/ui/javascript/gleeunit/gleeunit/should.mjs new file mode 100644 index 0000000..c34c700 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit/should.mjs @@ -0,0 +1,135 @@ +import * as $option from "../../gleam_stdlib/gleam/option.mjs"; +import { None, Some } from "../../gleam_stdlib/gleam/option.mjs"; +import * as $string from "../../gleam_stdlib/gleam/string.mjs"; +import { Ok, Error, toList, makeError, isEqual } from "../gleam.mjs"; + +const FILEPATH = "src/gleeunit/should.gleam"; + +export function equal(a, b) { + let $ = isEqual(a, b); + if ($) { + return undefined; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 10, + "equal", + $string.concat( + toList([ + "\n", + $string.inspect(a), + "\nshould equal\n", + $string.inspect(b), + ]), + ), + {} + ) + } +} + +export function not_equal(a, b) { + let $ = !isEqual(a, b); + if ($) { + return undefined; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 23, + "not_equal", + $string.concat( + toList([ + "\n", + $string.inspect(a), + "\nshould not equal\n", + $string.inspect(b), + ]), + ), + {} + ) + } +} + +export function be_ok(a) { + if (a instanceof Ok) { + let value = a[0]; + return value; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 35, + "be_ok", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be ok"])), + {} + ) + } +} + +export function be_error(a) { + if (a instanceof Error) { + let error = a[0]; + return error; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 42, + "be_error", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be error"])), + {} + ) + } +} + +export function be_some(a) { + if (a instanceof Some) { + let value = a[0]; + return value; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 49, + "be_some", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be some"])), + {} + ) + } +} + +export function be_none(a) { + if (a instanceof None) { + return undefined; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 56, + "be_none", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be none"])), + {} + ) + } +} + +export function be_true(actual) { + let _pipe = actual; + return equal(_pipe, true); +} + +export function be_false(actual) { + let _pipe = actual; + return equal(_pipe, false); +} + +export function fail() { + return be_true(false); +} diff --git a/ui/javascript/gleeunit/gleeunit@internal@gleam_panic.erl b/ui/javascript/gleeunit/gleeunit@internal@gleam_panic.erl new file mode 100644 index 0000000..398ea7d --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit@internal@gleam_panic.erl @@ -0,0 +1,56 @@ +-module(gleeunit@internal@gleam_panic). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/internal/gleam_panic.gleam"). +-export([from_dynamic/1]). +-export_type([gleam_panic/0, panic_kind/0, assert_kind/0, asserted_expression/0, expression_kind/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(false). + +-type gleam_panic() :: {gleam_panic, + binary(), + binary(), + binary(), + binary(), + integer(), + panic_kind()}. + +-type panic_kind() :: todo | + panic | + {let_assert, + integer(), + integer(), + integer(), + integer(), + gleam@dynamic:dynamic_()} | + {assert, integer(), integer(), integer(), assert_kind()}. + +-type assert_kind() :: {binary_operator, + binary(), + asserted_expression(), + asserted_expression()} | + {function_call, list(asserted_expression())} | + {other_expression, asserted_expression()}. + +-type asserted_expression() :: {asserted_expression, + integer(), + integer(), + expression_kind()}. + +-type expression_kind() :: {literal, gleam@dynamic:dynamic_()} | + {expression, gleam@dynamic:dynamic_()} | + unevaluated. + +-file("src/gleeunit/internal/gleam_panic.gleam", 49). +?DOC(false). +-spec from_dynamic(gleam@dynamic:dynamic_()) -> {ok, gleam_panic()} | + {error, nil}. +from_dynamic(Data) -> + gleeunit_gleam_panic_ffi:from_dynamic(Data). diff --git a/ui/javascript/gleeunit/gleeunit@internal@reporting.erl b/ui/javascript/gleeunit/gleeunit@internal@reporting.erl new file mode 100644 index 0000000..0b290a7 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit@internal@reporting.erl @@ -0,0 +1,343 @@ +-module(gleeunit@internal@reporting). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/internal/reporting.gleam"). +-export([new_state/0, finished/1, test_passed/1, test_failed/4, eunit_missing/0, test_skipped/3]). +-export_type([state/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(false). + +-type state() :: {state, integer(), integer(), integer()}. + +-file("src/gleeunit/internal/reporting.gleam", 15). +?DOC(false). +-spec new_state() -> state(). +new_state() -> + {state, 0, 0, 0}. + +-file("src/gleeunit/internal/reporting.gleam", 223). +?DOC(false). +-spec red(binary()) -> binary(). +red(Text) -> + <<<<"\x{001b}[31m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 215). +?DOC(false). +-spec yellow(binary()) -> binary(). +yellow(Text) -> + <<<<"\x{001b}[33m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 219). +?DOC(false). +-spec green(binary()) -> binary(). +green(Text) -> + <<<<"\x{001b}[32m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 19). +?DOC(false). +-spec finished(state()) -> integer(). +finished(State) -> + case State of + {state, 0, 0, 0} -> + gleam_stdlib:println(<<"\nNo tests found!"/utf8>>), + 1; + + {state, _, 0, 0} -> + Message = <<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, no failures"/utf8>>, + gleam_stdlib:println(green(Message)), + 0; + + {state, _, _, 0} -> + Message@1 = <<<<<<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary, + " failures"/utf8>>, + gleam_stdlib:println(red(Message@1)), + 1; + + {state, _, 0, _} -> + Message@2 = <<<<<<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, 0 failures, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary, + " skipped"/utf8>>, + gleam_stdlib:println(yellow(Message@2)), + 1; + + {state, _, _, _} -> + Message@3 = <<<<<<<<<<<<"\n"/utf8, + (erlang:integer_to_binary( + erlang:element(2, State) + ))/binary>>/binary, + " passed, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary, + " failures, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary, + " skipped"/utf8>>, + gleam_stdlib:println(red(Message@3)), + 1 + end. + +-file("src/gleeunit/internal/reporting.gleam", 66). +?DOC(false). +-spec test_passed(state()) -> state(). +test_passed(State) -> + gleam_stdlib:print(green(<<"."/utf8>>)), + {state, + erlang:element(2, State) + 1, + erlang:element(3, State), + erlang:element(4, State)}. + +-file("src/gleeunit/internal/reporting.gleam", 227). +?DOC(false). +-spec grey(binary()) -> binary(). +grey(Text) -> + <<<<"\x{001b}[90m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 100). +?DOC(false). +-spec format_unknown(binary(), binary(), gleam@dynamic:dynamic_()) -> binary(). +format_unknown(Module, Function, Error) -> + erlang:list_to_binary( + [<<(grey(<<<>/binary, Function/binary>>))/binary, + "\n"/utf8>>, + <<"An unexpected error occurred:\n"/utf8>>, + <<"\n"/utf8>>, + <<<<" "/utf8, (gleam@string:inspect(Error))/binary>>/binary, + "\n"/utf8>>] + ). + +-file("src/gleeunit/internal/reporting.gleam", 211). +?DOC(false). +-spec cyan(binary()) -> binary(). +cyan(Text) -> + <<<<"\x{001b}[36m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 191). +?DOC(false). +-spec code_snippet(gleam@option:option(bitstring()), integer(), integer()) -> binary(). +code_snippet(Src, Start, End) -> + _pipe = begin + gleam@result:'try'( + gleam@option:to_result(Src, nil), + fun(Src@1) -> + gleam@result:'try'( + gleam_stdlib:bit_array_slice(Src@1, Start, End - Start), + fun(Snippet) -> + gleam@result:'try'( + gleam@bit_array:to_string(Snippet), + fun(Snippet@1) -> + Snippet@2 = <<<<<<(cyan(<<" code"/utf8>>))/binary, + ": "/utf8>>/binary, + Snippet@1/binary>>/binary, + "\n"/utf8>>, + {ok, Snippet@2} + end + ) + end + ) + end + ) + end, + gleam@result:unwrap(_pipe, <<""/utf8>>). + +-file("src/gleeunit/internal/reporting.gleam", 207). +?DOC(false). +-spec bold(binary()) -> binary(). +bold(Text) -> + <<<<"\x{001b}[1m"/utf8, Text/binary>>/binary, "\x{001b}[22m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 183). +?DOC(false). +-spec inspect_value(gleeunit@internal@gleam_panic:asserted_expression()) -> binary(). +inspect_value(Value) -> + case erlang:element(4, Value) of + unevaluated -> + grey(<<"unevaluated"/utf8>>); + + {literal, Value@1} -> + gleam@string:inspect(Value@1); + + {expression, Value@2} -> + gleam@string:inspect(Value@2) + end. + +-file("src/gleeunit/internal/reporting.gleam", 179). +?DOC(false). +-spec assert_value( + binary(), + gleeunit@internal@gleam_panic:asserted_expression() +) -> binary(). +assert_value(Name, Value) -> + <<<<<<(cyan(Name))/binary, ": "/utf8>>/binary, + (inspect_value(Value))/binary>>/binary, + "\n"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 160). +?DOC(false). +-spec assert_info(gleeunit@internal@gleam_panic:assert_kind()) -> binary(). +assert_info(Kind) -> + case Kind of + {binary_operator, _, Left, Right} -> + erlang:list_to_binary( + [assert_value(<<" left"/utf8>>, Left), + assert_value(<<"right"/utf8>>, Right)] + ); + + {function_call, Arguments} -> + _pipe = Arguments, + _pipe@1 = gleam@list:index_map( + _pipe, + fun(E, I) -> + Number = gleam@string:pad_start( + erlang:integer_to_binary(I), + 5, + <<" "/utf8>> + ), + assert_value(Number, E) + end + ), + erlang:list_to_binary(_pipe@1); + + {other_expression, _} -> + <<""/utf8>> + end. + +-file("src/gleeunit/internal/reporting.gleam", 113). +?DOC(false). +-spec format_gleam_error( + gleeunit@internal@gleam_panic:gleam_panic(), + binary(), + binary(), + gleam@option:option(bitstring()) +) -> binary(). +format_gleam_error(Error, Module, Function, Src) -> + Location = grey( + <<<<(erlang:element(3, Error))/binary, ":"/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(6, Error)))/binary>> + ), + case erlang:element(7, Error) of + panic -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"panic"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + todo -> + erlang:list_to_binary( + [<<<<<<(bold(yellow(<<"todo"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + {assert, Start, End, _, Kind} -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"assert"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + code_snippet(Src, Start, End), + assert_info(Kind), + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + {let_assert, Start@1, End@1, _, _, Value} -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"let assert"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + code_snippet(Src, Start@1, End@1), + <<<<<<(cyan(<<"value"/utf8>>))/binary, ": "/utf8>>/binary, + (gleam@string:inspect(Value))/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ) + end. + +-file("src/gleeunit/internal/reporting.gleam", 71). +?DOC(false). +-spec test_failed(state(), binary(), binary(), gleam@dynamic:dynamic_()) -> state(). +test_failed(State, Module, Function, Error) -> + Message = case gleeunit_gleam_panic_ffi:from_dynamic(Error) of + {ok, Error@1} -> + Src = gleam@option:from_result( + file:read_file(erlang:element(3, Error@1)) + ), + format_gleam_error(Error@1, Module, Function, Src); + + {error, _} -> + format_unknown(Module, Function, Error) + end, + gleam_stdlib:print(<<"\n"/utf8, Message/binary>>), + {state, + erlang:element(2, State), + erlang:element(3, State) + 1, + erlang:element(4, State)}. + +-file("src/gleeunit/internal/reporting.gleam", 89). +?DOC(false). +-spec eunit_missing() -> {ok, any()} | {error, nil}. +eunit_missing() -> + Message = <<(bold(red(<<"Error"/utf8>>)))/binary, + ": EUnit libraries not found. + +Your Erlang installation seems to be incomplete. If you installed Erlang using +a package manager ensure that you have installed the full Erlang +distribution instead of a stripped-down version. +"/utf8>>, + gleam_stdlib:print_error(Message), + {error, nil}. + +-file("src/gleeunit/internal/reporting.gleam", 202). +?DOC(false). +-spec test_skipped(state(), binary(), binary()) -> state(). +test_skipped(State, Module, Function) -> + gleam_stdlib:print( + <<<<<<<<"\n"/utf8, Module/binary>>/binary, "."/utf8>>/binary, + Function/binary>>/binary, + (yellow(<<" skipped"/utf8>>))/binary>> + ), + {state, + erlang:element(2, State), + erlang:element(3, State), + erlang:element(4, State) + 1}. diff --git a/ui/javascript/gleeunit/gleeunit@should.erl b/ui/javascript/gleeunit/gleeunit@should.erl new file mode 100644 index 0000000..fca394b --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit@should.erl @@ -0,0 +1,153 @@ +-module(gleeunit@should). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/should.gleam"). +-export([equal/2, not_equal/2, be_ok/1, be_error/1, be_some/1, be_none/1, be_true/1, be_false/1, fail/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(" Use the `assert` keyword instead of this module.\n"). + +-file("src/gleeunit/should.gleam", 6). +-spec equal(DOK, DOK) -> nil. +equal(A, B) -> + case A =:= B of + true -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould equal\n"/utf8>>, + gleam@string:inspect(B)] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"equal"/utf8>>, + line => 10}) + end. + +-file("src/gleeunit/should.gleam", 19). +-spec not_equal(DOL, DOL) -> nil. +not_equal(A, B) -> + case A /= B of + true -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould not equal\n"/utf8>>, + gleam@string:inspect(B)] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"not_equal"/utf8>>, + line => 23}) + end. + +-file("src/gleeunit/should.gleam", 32). +-spec be_ok({ok, DOM} | {error, any()}) -> DOM. +be_ok(A) -> + case A of + {ok, Value} -> + Value; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be ok"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_ok"/utf8>>, + line => 35}) + end. + +-file("src/gleeunit/should.gleam", 39). +-spec be_error({ok, any()} | {error, DOR}) -> DOR. +be_error(A) -> + case A of + {error, Error} -> + Error; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be error"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_error"/utf8>>, + line => 42}) + end. + +-file("src/gleeunit/should.gleam", 46). +-spec be_some(gleam@option:option(DOU)) -> DOU. +be_some(A) -> + case A of + {some, Value} -> + Value; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be some"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_some"/utf8>>, + line => 49}) + end. + +-file("src/gleeunit/should.gleam", 53). +-spec be_none(gleam@option:option(any())) -> nil. +be_none(A) -> + case A of + none -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be none"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_none"/utf8>>, + line => 56}) + end. + +-file("src/gleeunit/should.gleam", 60). +-spec be_true(boolean()) -> nil. +be_true(Actual) -> + _pipe = Actual, + equal(_pipe, true). + +-file("src/gleeunit/should.gleam", 65). +-spec be_false(boolean()) -> nil. +be_false(Actual) -> + _pipe = Actual, + equal(_pipe, false). + +-file("src/gleeunit/should.gleam", 70). +-spec fail() -> nil. +fail() -> + be_true(false). diff --git a/ui/javascript/gleeunit/gleeunit_ffi.erl b/ui/javascript/gleeunit/gleeunit_ffi.erl new file mode 100644 index 0000000..05c7490 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit_ffi.erl @@ -0,0 +1,21 @@ +-module(gleeunit_ffi). + +-export([find_files/2, run_eunit/2]). + +find_files(Pattern, In) -> + Results = filelib:wildcard(binary_to_list(Pattern), binary_to_list(In)), + lists:map(fun list_to_binary/1, Results). + +run_eunit(Tests, Options) -> + case code:which(eunit) of + non_existing -> + gleeunit@internal@reporting:eunit_missing(); + + _ -> + case eunit:test(Tests, Options) of + ok -> {ok, nil}; + error -> {error, nil}; + {error, Term} -> {error, Term} + end + end. + diff --git a/ui/javascript/gleeunit/gleeunit_ffi.mjs b/ui/javascript/gleeunit/gleeunit_ffi.mjs new file mode 100644 index 0000000..7bdc071 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit_ffi.mjs @@ -0,0 +1,100 @@ +import { readFileSync } from "node:fs"; +import { Result$Ok, Result$Error } from "./gleam.mjs"; +import * as reporting from "./gleeunit/internal/reporting.mjs"; + +export function read_file(path) { + try { + return Result$Ok(readFileSync(path)); + } catch { + return Result$Error(undefined); + } +} + +async function* gleamFiles(directory) { + for (let entry of await read_dir(directory)) { + let path = join_path(directory, entry); + if (path.endsWith(".gleam")) { + yield path; + } else { + try { + yield* gleamFiles(path); + } catch (error) { + // Could not read directory, assume it's a file + } + } + } +} + +async function readRootPackageName() { + let toml = await async_read_file("gleam.toml", "utf-8"); + for (let line of toml.split("\n")) { + let matches = line.match(/\s*name\s*=\s*"([a-z][a-z0-9_]*)"/); // Match regexp in compiler-cli/src/new.rs in validate_name() + if (matches) return matches[1]; + } + throw new Error("Could not determine package name from gleam.toml"); +} + +export async function main() { + let state = reporting.new_state(); + + let packageName = await readRootPackageName(); + let dist = `../${packageName}/`; + + for await (let path of await gleamFiles("test")) { + let js_path = path.slice("test/".length).replace(".gleam", ".mjs"); + let module = await import(join_path(dist, js_path)); + for (let fnName of Object.keys(module)) { + if (!fnName.endsWith("_test")) continue; + try { + await module[fnName](); + state = reporting.test_passed(state); + } catch (error) { + let moduleName = js_path.slice(0, -4); + state = reporting.test_failed(state, moduleName, fnName, error); + } + } + } + + const status = reporting.finished(state); + exit(status); +} + +export function crash(message) { + throw new Error(message); +} + +function exit(code) { + if (globalThis.Deno) { + Deno.exit(code); + } else { + process.exit(code); + } +} + +async function read_dir(path) { + if (globalThis.Deno) { + let items = []; + for await (let item of Deno.readDir(path, { withFileTypes: true })) { + items.push(item.name); + } + return items; + } else { + let { readdir } = await import("node:fs/promises"); + return readdir(path); + } +} + +function join_path(a, b) { + if (a.endsWith("/")) return a + b; + return a + "/" + b; +} + +async function async_read_file(path) { + if (globalThis.Deno) { + return Deno.readTextFile(path); + } else { + let { readFile } = await import("node:fs/promises"); + let contents = await readFile(path); + return contents.toString(); + } +} diff --git a/ui/javascript/gleeunit/gleeunit_progress.erl b/ui/javascript/gleeunit/gleeunit_progress.erl new file mode 100644 index 0000000..e6576a5 --- /dev/null +++ b/ui/javascript/gleeunit/gleeunit_progress.erl @@ -0,0 +1,72 @@ +%% A formatter adapted from Sean Cribb's https://github.com/seancribbs/eunit_formatters + +-module(gleeunit_progress). +-define(NOTEST, true). + +%% eunit_listener callbacks +-export([ + init/1, handle_begin/3, handle_end/3, handle_cancel/3, terminate/2, + start/0, start/1 +]). + +-define(reporting, gleeunit@internal@reporting). + +start() -> + start([]). + +start(Options) -> + eunit_listener:start(?MODULE, Options). + +init(_Options) -> + ?reporting:new_state(). + +handle_begin(_test_or_group, _data, State) -> + State. + +handle_end(group, _data, State) -> + State; +handle_end(test, Data, State) -> + {AtomModule, AtomFunction, _Arity} = proplists:get_value(source, Data), + Module = erlang:atom_to_binary(AtomModule), + Function = erlang:atom_to_binary(AtomFunction), + + % EUnit swallows stdout, so print it to make debugging easier. + case proplists:get_value(output, Data) of + undefined -> ok; + <<>> -> ok; + Out -> gleam@io:print(Out) + end, + + case proplists:get_value(status, Data) of + ok -> + ?reporting:test_passed(State); + {skipped, _Reason} -> + ?reporting:test_skipped(State, Module, Function); + {error, {_, Exception, _Stack}} -> + ?reporting:test_failed(State, Module, Function, Exception) + end. + + +handle_cancel(_test_or_group, Data, State) -> + ?reporting:test_failed(State, <<"gleeunit">>, <<"main">>, Data). + +terminate({ok, _Data}, State) -> + ?reporting:finished(State), + ok; +terminate({error, Reason}, State) -> + ?reporting:finished(State), + io:fwrite(" +Eunit failed: + +~80p + +This is probably a bug in gleeunit. Please report it. +", [Reason]), + sync_end(error). + +sync_end(Result) -> + receive + {stop, Reference, ReplyTo} -> + ReplyTo ! {result, Reference, Result}, + ok + end. diff --git a/ui/javascript/markdown2/_gleam_artefacts/definition.cache b/ui/javascript/markdown2/_gleam_artefacts/definition.cache new file mode 100644 index 0000000000000000000000000000000000000000..25a82c1f110589e76db788d4e76b44851feed0fb GIT binary patch literal 8194 zcmeHMO^6&t7~MaO2E~ICAs`;~pdNOT#aumTOkCVeVs;}Q^f1}!-JR_GWu^z+qXay8 zP|>TmfaIp2;zhxP+!R#2D5#jL7eTy;;Ct2ex~jUmCk>m~Af(_;Rae#b|Ml0^JqKs9 z)=D{B}_jrA;o$WS^akuT4d!2SK;l(lFIB<{`Xd$C)F8hs`#w0O& z$3QBqSi$#W=`av;aWvVehTtM|$Krq;o=6gyyv3^zfXl!wkzB%`GO~7;?bp&~YP@{| z6Mq7?M0CX6N<(*#9nRbSRJ)_aSquoalI58)SNb&my7*+=+^;m->LkssBN(M3n8RL; zNme7o6LL%9ayOHuuXC_L`f!uqw5hhzA`S@U(&b*Nb?9=R9dEaX&5iBdk@CT9!6@J( zEmzvrUK4ddSgwE)E**~3?fn-o`&p217w*3Tvj5_^pC(p(c}6jBJOSM1YWcP`z04gnwZ+pZ{y4BPUsKHKjHj|nNGup>q; zQ>4U+W@qGPmCA)^PzoE9ps)l*u4DFlKsTv7gDlW=s_LtY%e+YoQ?x5m%xsoo`H?6V zCdIs?0!e4*atE;vNHm4zj>+o7GN#S}F>9k)Z99}UaTPJbYnUPq?wm(TC?FT1SQuXT z;gmOxO_wn9E>LDiMLZ&%r1BIF{Jt2D?}!RK61am=WGkzwb&kej7NjJCudwsPok<8wsy&Ing0%#Q6KI~< z!z?nHBo^pvlr`Pjg|kG?qy~%f;yUY|6{Ut#LJj8Sv~rnz5#DNK9j|cKZ{j?SyGqi- zf9P2F^3DeorfGihx9h&*JW3+*Lp2KNLJx(xJX+=>#B$HO<5@vl`peZoa=dW0=4m+#24LIyw<*= zJ=zzfmtx=_yZ%RnkH{$WEY19u#-93|R{=&prci?D z<=P128}?6?BuXRi>+>aSeyfr|$?jfyv2|wV3h*&7SuuUjA;kU;@ICNZxCava|l z|EVOA!~Adbd_8!zl0wF;h98};gW#J=B8jozbg(b`u=ALu(4TkJ@d<4D17!S9vD^)1 z!VY0K|9T~bLLMF0weUDLJps^#58&rPfa9m|vuMXB(Z6WN=h1K4v4j`vCYDWm8^7<^ zacr%eYu};x4S?-K;4(n%UjteI8A@Se2^ZN=-Q>b9x literal 0 HcmV?d00001 diff --git a/ui/javascript/markdown2/_gleam_artefacts/definition.cache_meta b/ui/javascript/markdown2/_gleam_artefacts/definition.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..8631eed417beef843017aa467bbc6cbe4922b43a GIT binary patch literal 185 zcmazLbI4)<0;LyOOpJ^m77(yND2CkplvEIt8;DP(_Xeq5JUKxYDhHx?fS4DEL1IQg zYy!llKP`XUw|tW@-&_@I_P#0SL({hgU}W_EWrTJXZ3d*|GH z&iS8n&%JYpoV3;5s9Fi14epcAp>Aj-3|CZ?OL~~ps`u_!FyFK_ic3l-DfM}p1CwnMIVEdB z2|Nm>zzmR#p9JrN0b<+_J_cvO2w{$ctza9F&`*FqMesM+z_vaJ9s>KpJU9)$1mA+o;0N$07$J!dfPLTv za28wvw{aFm!8&j^*a$WOIsYzr5u5}c0XaKY!Rc>D$JZrCC^ExgXwe*D3bKXprtaFt1r$E{Q_Fq;X8?awe9~bDq*6}R%1NAY0$ze6C(@uMu z__N2e7V#S_d|MYAxjfsZzN^*DG&Vw|taMBx&yiiP{4ATvpT-=0LdrkWwA-SrRLzgv z+@^4Z(2oIer39U{hHUd@-JnwQB2ANXFN6E_qn|n()>_MqQ!1wV1aBsOcjoJJ99;O% zEXdE+G$Je=UMZa3_qy?%VDKc6z)oZ0Lm*~#`_hx636@8TxqJkjEkM34@%dJncE=%@ zK)OR`R#xdKh!05DqNc2{Pa3ej7L3_f$iD$X+T7$NVVjrM!!4|l_C1mO1f=z&IHVDu zpC|$nkkj$M4dg(eXw4?{wS;&q0mDxaL_@$Ih;> zVZr@|qiZYXP%SxK1y#XNRgEd>`Hf%|{-%k=*MY$u!$~E#FnD_Ke nWI9!J=7BWxlIxh4_Pb#xob~`|9pPC(ZHM{KP&@L9Ki8c>c0*&BDAr|Nu-q~G?5S$VX{ExTbffy^F-iKrIFXn^8<8 zx=BnO>|$gSiF;JHqQY8D-k2u^YQB?jwg zl`64gmOWkGkPMf4cyG|ywijh82)USCK@Q76c98WYRB(lGNU0odQAMOlscFn%9t$|Y zDQ-~1J9?Y>X~Z#wEOv2%d%WQjUl^c^DXifbXE?_l9`S>b7NsVTMh+!h-~mr~#tXg? np#|eu#v!VBMGvnQ!w?LNB7r1K9HD}1)bN2Quh|!%9e?o)pZY=| literal 0 HcmV?d00001 diff --git a/ui/javascript/markdown2/_gleam_artefacts/markdown2_test.cache b/ui/javascript/markdown2/_gleam_artefacts/markdown2_test.cache new file mode 100644 index 0000000000000000000000000000000000000000..b46ee53bdcac4ad697cae72e11eb7b1a8c74ffa7 GIT binary patch literal 1139 zcmb_a%}T^D5bpYif-ZXV;7JfXZCpJFq8ArEDu|w!x{Yhyv{}-W#fu2OgU_SrqlnMp zOqy?hYU{~?k4)z0o5@U?K_N+K29X}6`&}zD+aTjg+2ilQf>&^!YOUbo#JQ6H+T;@c zV9{&mp~@!KRd2nqcO4zcp^j6F6sCkMdF4uGxw0tqu~wKr{W;wPVlD^ZVG9FUq@t-a zW3(y9ftBLs2`m@!%!83p;r=y@B#hI7r04=eboQqA%q|1r8>H;GOM|3HM`^@NpLkVY z$uOzvZQV!TOQm#oq>YN)_Cdl^s*U-(*FL2_Gmq=^A9`gj7L;Y3d*h+(5{?MRgcAZ6 ze<8dQ-U#o6FT%G69Zov_6edqaBBMAI58eR z&lCjsq3u??-P#+QpQejqk=I&BqT(Yu@vDi5LrfLIWS kHGtR(h;4w_7Kqbtq>7t`8e&GeIN^SBnQq9ew3A3P}#wl3i>Tg`4~!nIGO34W7sG}J~3LLpD0OT zGB$d|el0{^#@^BP_x$sYvYd)A$>D^^A^NRWDN9IQ#bqy=HMk+ni1OsyjJ z2})Csf}E06d6CSuPS7@KuGF66BmXeCq`PcHM|#(a-Z4QceQHOC;fMkpH=?7+YejE4 za7RMz;t*x?qXOWnin5a$0Ajyt{x1%F!aREJ$L&?TVLQBn3)Luqbi6@ql{c) w8;twu=LR_5+$y+!8-VwSl^;kQj-#kn1{nR!VCd{4Va-heJO3E?d&WK(zjxf64FCWD literal 0 HcmV?d00001 diff --git a/ui/javascript/markdown2/_gleam_artefacts/mode.cache_meta b/ui/javascript/markdown2/_gleam_artefacts/mode.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..c414e03d175844e2cd13e45fbeaa41e00ad86e0c GIT binary patch literal 77 zcmazLbI4)<0;LyOOpG7~2>kvyK_l*}`Du0t7fcEONhu&!0%9E?HUVNYC4TsYrL={jQi_83oqNwWyR*A_Qt-gH_uPBW zIrsd{o!Jq`QtT$Fmk2+qD@`|PRHJryzs{Yt%5Z!liURHpfmzeDzdLDp+;`R~x5W+8 z&gu=ix+km6L{%@2{4k@6m?VuT^R$SiZ}odkAFRmp$%_Pu)bD^MU6! zX>wPV_~CrFSb;C-u(aF!H2YVFT%%{y-LM+izLFIQymyO!7DGP($>XBp{P&^vb@+AW zAjheCH9zz-KMFgE2tD2GJQ?k}o240km?8;_M)S>8YI-x2W{l$h5*V|+H4OZ&&=}(^ z8I0q=MxX@D0DA!GBPq=ZU>Udyj3b(x0Eui1-~mT~CEzkJf%Mz~YzDRhHQ*rdF|Z6= z0_1sl6v%)@;1F;Wcn|mz_!{^g_yM>A{0ZEHB0UVe27CmZ0oJ0Or+^oLmw_eVXW#-b zhS(nh_5!a1hk*Bi6<`EiIR)$ho(1*-ZQvDP5%>i74)_iDLjkW1P3u@nT4AZ_ReiU# z-(4u}b1RKjoR-q0Qp&t$T*@z0vt-+S(mbkapmQlqP2M*-m9Hu@gmVocPD4#xnQUES zr&RST8CN>Z%v;vVq~<;-t|jJPWeZz5T*WkH7Xn-goAl^z7wFckpSYF!0xE834azzy zr3KMY$0yXo5MB~lohqM#Ijs{4n6uT4GuJ@f4Q31#VVux-pOV7PeN@9s{*m)7Tt+V^CocM&1A5RSdA&N1t@c6eUYnC$~b zuo14`F7B@q!S!~_Ni#Re(sJ9+>gu3#6$XbjO`9;@t^6#;UCPH84=7K)?xH=_in6@I zD{$(pI@TXD-7l#83h|3QDxGH4xEz$2o&Fgz_qk^8C&YG6%!&E(qLB~490TOE z6^v(F`%L|G)`1z-36CnAYd%9MU4!k0+8@+ov|#QGD(%N=q&tI@ z=oW6&F^N=1yejnEyGYPg1asePyhUUHe(SH%b3#y>~ zp%)bUHvd#nDQ7(#odCqzCEyAmzVp1!)4}t0M){-t3#^=1OZ^H=$Ie!oU-!?z%JXW8 zq1%w``-on*!Zwn@Bt8I*Y1LZ&oZa3_`TvBIkUpgv7cpL5Ra=&w?%kMvGbHdR8 literal 0 HcmV?d00001 diff --git a/ui/javascript/markdown2/_gleam_artefacts/parser.cache_meta b/ui/javascript/markdown2/_gleam_artefacts/parser.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..9703df7f11dfd7cb5c9ae5b0f2fa664a9b7ec1cc GIT binary patch literal 516 zcmZY6%_~Gv7{~Ev7^9H|6Ip1K4I7zRlA1yZ$%?Yl$bISP&YhZjq((}!A~j2SDSrVQ z{sXg_EW|?DiLI0*O|$Yn=FVdB)Tev;J@=gRbZ;#)y(QGy;p9N1)2yhj#zQRIQNGeD z@0*|((Xf-tTXuqV67eu|J$oaUQobG{7)N(YqhJTyo=%a?pm{}OlcxebOSO;#T_2-h#%B3#@C#}J}&W$XopA|>nNgvH@u@K jCNhC64p7Dgo)F{TrLl}PWU+%ilyHN4eBnF9)PMC0!GcR5 literal 0 HcmV?d00001 diff --git a/ui/javascript/markdown2/definition.mjs b/ui/javascript/markdown2/definition.mjs new file mode 100644 index 0000000..611061f --- /dev/null +++ b/ui/javascript/markdown2/definition.mjs @@ -0,0 +1,103 @@ +import { CustomType as $CustomType } from "./gleam.mjs"; +import * as $mode from "./mode.mjs"; + +export class Line extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Line$Line = ($0) => new Line($0); +export const Line$isLine = (value) => value instanceof Line; +export const Line$Line$0 = (value) => value[0]; + +export class Text extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Inline$Text = ($0) => new Text($0); +export const Inline$isText = (value) => value instanceof Text; +export const Inline$Text$0 = (value) => value[0]; + +export class Push extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Inline$Push = ($0) => new Push($0); +export const Inline$isPush = (value) => value instanceof Push; +export const Inline$Push$0 = (value) => value[0]; + +export class Pop extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Inline$Pop = ($0) => new Pop($0); +export const Inline$isPop = (value) => value instanceof Pop; +export const Inline$Pop$0 = (value) => value[0]; + +export class Hirachy extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Feeling$Hirachy = ($0) => new Hirachy($0); +export const Feeling$isHirachy = (value) => value instanceof Hirachy; +export const Feeling$Hirachy$0 = (value) => value[0]; + +export class Truth extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Feeling$Truth = ($0) => new Truth($0); +export const Feeling$isTruth = (value) => value instanceof Truth; +export const Feeling$Truth$0 = (value) => value[0]; + +export class Marking extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Feeling$Marking = ($0) => new Marking($0); +export const Feeling$isMarking = (value) => value instanceof Marking; +export const Feeling$Marking$0 = (value) => value[0]; + +export class Quote extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Feeling$Quote = ($0) => new Quote($0); +export const Feeling$isQuote = (value) => value instanceof Quote; +export const Feeling$Quote$0 = (value) => value[0]; + +export class State extends $CustomType { + constructor(hirachystate, truthstate, markingstate, quotestate) { + super(); + this.hirachystate = hirachystate; + this.truthstate = truthstate; + this.markingstate = markingstate; + this.quotestate = quotestate; + } +} +export const State$State = (hirachystate, truthstate, markingstate, quotestate) => + new State(hirachystate, truthstate, markingstate, quotestate); +export const State$isState = (value) => value instanceof State; +export const State$State$hirachystate = (value) => value.hirachystate; +export const State$State$0 = (value) => value.hirachystate; +export const State$State$truthstate = (value) => value.truthstate; +export const State$State$1 = (value) => value.truthstate; +export const State$State$markingstate = (value) => value.markingstate; +export const State$State$2 = (value) => value.markingstate; +export const State$State$quotestate = (value) => value.quotestate; +export const State$State$3 = (value) => value.quotestate; diff --git a/ui/javascript/markdown2/gleam.mjs b/ui/javascript/markdown2/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/ui/javascript/markdown2/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/ui/javascript/markdown2/markdown2.mjs b/ui/javascript/markdown2/markdown2.mjs new file mode 100644 index 0000000..1ff5827 --- /dev/null +++ b/ui/javascript/markdown2/markdown2.mjs @@ -0,0 +1,77 @@ +import * as $json from "../gleam_json/gleam/json.mjs"; +import * as $int from "../gleam_stdlib/gleam/int.mjs"; +import * as $io from "../gleam_stdlib/gleam/io.mjs"; +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $definition from "./definition.mjs"; +import { toList } from "./gleam.mjs"; +import * as $mode from "./mode.mjs"; +import * as $parser from "./parser.mjs"; + +function inline_to_json(inline) { + if (inline instanceof $definition.Text) { + let text = inline[0]; + return $json.object( + toList([["type", $json.string("text")], ["value", $json.string(text)]]), + ); + } else if (inline instanceof $definition.Push) { + let feeling = inline[0]; + if (feeling instanceof $definition.Hirachy) { + let level = feeling[0]; + return $json.object( + toList([["type", $json.string("hirachy")], ["value", $json.int(level)]]), + ); + } else if (feeling instanceof $definition.Truth) { + let level = feeling[0]; + return $json.object( + toList([["type", $json.string("truth")], ["value", $json.int(level)]]), + ); + } else if (feeling instanceof $definition.Marking) { + let level = feeling[0]; + return $json.object( + toList([["type", $json.string("marking")], ["value", $json.int(level)]]), + ); + } else { + let level = feeling[0]; + return $json.object( + toList([["type", $json.string("quote")], ["value", $json.int(level)]]), + ); + } + } else { + let text = inline[0]; + return $json.object( + toList([ + ["type", $json.string("pop")], + [ + "value", + (() => { + if (text instanceof $mode.Hirachy) { + return $json.string("Hirachy"); + } else if (text instanceof $mode.Truth) { + return $json.string("Truth"); + } else if (text instanceof $mode.Marking) { + return $json.string("Marking"); + } else { + return $json.string("Quote"); + } + })(), + ], + ]), + ); + } +} + +function line_to_json(line) { + let inlines = line[0]; + let _pipe = $json.array(inlines, inline_to_json); + return $json.to_string(_pipe); +} + +export function main() { + let input = "ljiosfgdjiosdfgj\n @2! A slightly important statement !1 I believe is wrong, !- quoted inside another quote.\n !3 A slightly important statement I believe is wrong, quoted inside another quote.\n #3!2@2 A slightly important statement I believe is wrong, quoted inside another quote.\n\n !2@3 important trusted but @- important !5 more important !- 2 important and !- normal"; + $io.print(line_to_json($parser.handler(input))); + return undefined; +} + +export function testing(input) { + return line_to_json($parser.handler(input)); +} diff --git a/ui/javascript/markdown2/markdown2_test.mjs b/ui/javascript/markdown2/markdown2_test.mjs new file mode 100644 index 0000000..83e2af9 --- /dev/null +++ b/ui/javascript/markdown2/markdown2_test.mjs @@ -0,0 +1,34 @@ +import * as $gleeunit from "../gleeunit/gleeunit.mjs"; +import { makeError } from "./gleam.mjs"; + +const FILEPATH = "test/markdown2_test.gleam"; + +export function main() { + return $gleeunit.main(); +} + +export function hello_world_test() { + let name = "Joe"; + let greeting = ("Hello, " + name) + "!"; + let $ = "Hello, Joe!"; + if (!(greeting === $)) { + throw makeError( + "assert", + FILEPATH, + "markdown2_test", + 12, + "hello_world_test", + "Assertion failed.", + { + kind: "binary_operator", + operator: "==", + left: { kind: "expression", value: greeting, start: 202, end: 210 }, + right: { kind: "literal", value: $, start: 214, end: 227 }, + start: 195, + end: 227, + expression_start: 202 + } + ) + } + return undefined; +} diff --git a/ui/javascript/markdown2/mode.mjs b/ui/javascript/markdown2/mode.mjs new file mode 100644 index 0000000..c74b587 --- /dev/null +++ b/ui/javascript/markdown2/mode.mjs @@ -0,0 +1,17 @@ +import { CustomType as $CustomType } from "./gleam.mjs"; + +export class Hirachy extends $CustomType {} +export const Type$Hirachy = () => new Hirachy(); +export const Type$isHirachy = (value) => value instanceof Hirachy; + +export class Truth extends $CustomType {} +export const Type$Truth = () => new Truth(); +export const Type$isTruth = (value) => value instanceof Truth; + +export class Marking extends $CustomType {} +export const Type$Marking = () => new Marking(); +export const Type$isMarking = (value) => value instanceof Marking; + +export class Quote extends $CustomType {} +export const Type$Quote = () => new Quote(); +export const Type$isQuote = (value) => value instanceof Quote; diff --git a/ui/javascript/markdown2/parser.mjs b/ui/javascript/markdown2/parser.mjs new file mode 100644 index 0000000..420ae5b --- /dev/null +++ b/ui/javascript/markdown2/parser.mjs @@ -0,0 +1,367 @@ +import * as $dict from "../gleam_stdlib/gleam/dict.mjs"; +import * as $stdlib$dict from "../gleam_stdlib/gleam/dict.mjs"; +import * as $int from "../gleam_stdlib/gleam/int.mjs"; +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $result from "../gleam_stdlib/gleam/result.mjs"; +import * as $string from "../gleam_stdlib/gleam/string.mjs"; +import * as $definition from "./definition.mjs"; +import { + Ok, + toList, + Empty as $Empty, + NonEmpty as $NonEmpty, + CustomType as $CustomType, + bitArraySlice, + bitArraySliceToInt, + BitArray as $BitArray, + List as $List, + UtfCodepoint as $UtfCodepoint, +} from "./gleam.mjs"; +import * as $mode from "./mode.mjs"; + +function whatsthat(mod) { + let modes = $dict.from_list( + toList([ + [ + "!", + [ + new $mode.Marking(), + (var0) => { return new $definition.Marking(var0); }, + ], + ], + [ + "@", + [new $mode.Truth(), (var0) => { return new $definition.Truth(var0); }], + ], + [ + ">", + [new $mode.Quote(), (var0) => { return new $definition.Quote(var0); }], + ], + [ + "#", + [ + new $mode.Hirachy(), + (var0) => { return new $definition.Hirachy(var0); }, + ], + ], + ]), + ); + let $ = $string.pop_grapheme(mod); + if ($ instanceof Ok) { + let prefix = $[0][0]; + let rest = $[0][1]; + let $1 = $dict.get(modes, prefix); + if ($1 instanceof Ok) { + let m = $1[0][0]; + let make_definition = $1[0][1]; + if (rest === "-") { + return new $definition.Pop(m); + } else { + let _block; + let _pipe = $int.parse(rest); + _block = $result.unwrap(_pipe, 1); + let value = _block; + return new $definition.Push(make_definition(value)); + } + } else { + return new $definition.Text(mod); + } + } else { + return new $definition.Text(mod); + } +} + +function parse(modificators) { + let _pipe = modificators; + let _pipe$1 = $string.replace(_pipe, "#", " |#"); + let _pipe$2 = $string.replace(_pipe$1, "@", " |@"); + let _pipe$3 = $string.replace(_pipe$2, "!", " |!"); + let _pipe$4 = $string.replace(_pipe$3, ">", " |>"); + let _pipe$5 = $string.split(_pipe$4, " |"); + let _pipe$6 = $list.flat_map( + _pipe$5, + (block) => { + let $ = (($string.starts_with(block, "#") || $string.starts_with( + block, + "@", + )) || $string.starts_with(block, "!")) || $string.starts_with(block, ">"); + if ($) { + let $1 = $string.split_once(block, " "); + if ($1 instanceof Ok) { + let tag = $1[0][0]; + let rest = $1[0][1]; + return toList([tag, rest]); + } else { + return toList([block]); + } + } else { + return toList([block]); + } + }, + ); + let _pipe$7 = $list.map(_pipe$6, $string.trim); + return $list.filter(_pipe$7, (x) => { return x !== ""; }); +} + +export function handler(line) { + let result = parse(line); + let result$1 = echo( + $list.map(result, whatsthat), + undefined, + "src/parser.gleam", + 10, + ); + return new $definition.Line(result$1); +} + +function echo(value, message, file, line) { + const grey = "\u001b[90m"; + const reset_color = "\u001b[39m"; + const file_line = `${file}:${line}`; + const inspector = new Echo$Inspector(); + const string_value = inspector.inspect(value); + const string_message = message === undefined ? "" : " " + message; + + if (globalThis.process?.stderr?.write) { + // If we're in Node.js, use `stderr` + const string = `${grey}${file_line}${reset_color}${string_message}\n${string_value}\n`; + globalThis.process.stderr.write(string); + } else if (globalThis.Deno) { + // If we're in Deno, use `stderr` + const string = `${grey}${file_line}${reset_color}${string_message}\n${string_value}\n`; + globalThis.Deno.stderr.writeSync(new TextEncoder().encode(string)); + } else { + // Otherwise, use `console.log` + // The browser's console.log doesn't support ansi escape codes + const string = `${file_line}${string_message}\n${string_value}`; + globalThis.console.log(string); + } + + return value; +} + +class Echo$Inspector { + #references = new globalThis.Set(); + + #isDict(value) { + try { + // We can only check if an object is a stdlib Dict if it is one of the + // project's dependencies. + // We import the public gleam/dict module, so to check if something is a + // dict we compare the `constructor` field on the object with that of a + // new dict. + const empty_dict = $stdlib$dict.new$(); + const dict_class = empty_dict.constructor; + return value instanceof dict_class; + } catch { + // If stdlib is not one of the project's dependencies then `$stdlib$dict` + // will not have been imported and the check will throw an exception meaning + // we can't check if something is actually a `Dict`. + return false; + } + } + + #float(float) { + const string = float.toString().replace("+", ""); + if (string.indexOf(".") >= 0) { + return string; + } else { + const index = string.indexOf("e"); + if (index >= 0) { + return string.slice(0, index) + ".0" + string.slice(index); + } else { + return string + ".0"; + } + } + } + + inspect(v) { + const t = typeof v; + if (v === true) return "True"; + if (v === false) return "False"; + if (v === null) return "//js(null)"; + if (v === undefined) return "Nil"; + if (t === "string") return this.#string(v); + if (t === "bigint" || globalThis.Number.isInteger(v)) return v.toString(); + if (t === "number") return this.#float(v); + if (v instanceof $UtfCodepoint) return this.#utfCodepoint(v); + if (v instanceof $BitArray) return this.#bit_array(v); + if (v instanceof globalThis.RegExp) return `//js(${v})`; + if (v instanceof globalThis.Date) return `//js(Date("${v.toISOString()}"))`; + if (v instanceof globalThis.Error) return `//js(${v.toString()})`; + if (v instanceof globalThis.Function) { + const args = []; + for (const i of globalThis.Array(v.length).keys()) + args.push(globalThis.String.fromCharCode(i + 97)); + return `//fn(${args.join(", ")}) { ... }`; + } + + if (this.#references.size === this.#references.add(v).size) { + return "//js(circular reference)"; + } + + let printed; + if (globalThis.Array.isArray(v)) { + printed = `#(${v.map((v) => this.inspect(v)).join(", ")})`; + } else if (v instanceof $List) { + printed = this.#list(v); + } else if (v instanceof $CustomType) { + printed = this.#customType(v); + } else if (this.#isDict(v)) { + printed = this.#dict(v); + } else if (v instanceof Set) { + return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`; + } else { + printed = this.#object(v); + } + this.#references.delete(v); + return printed; + } + + #object(v) { + const name = + globalThis.Object.getPrototypeOf(v)?.constructor?.name || "Object"; + const props = []; + for (const k of globalThis.Object.keys(v)) { + props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`); + } + const body = props.length ? " " + props.join(", ") + " " : ""; + const head = name === "Object" ? "" : name + " "; + return `//js(${head}{${body}})`; + } + + #dict(map) { + let body = "dict.from_list(["; + let first = true; + + let key_value_pairs = $stdlib$dict.fold(map, [], (pairs, key, value) => { + pairs.push([key, value]); + return pairs; + }); + + key_value_pairs.sort(); + key_value_pairs.forEach(([key, value]) => { + if (!first) body = body + ", "; + body = body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")"; + first = false; + }); + return body + "])"; + } + + #customType(record) { + const props = globalThis.Object.keys(record) + .map((label) => { + const value = this.inspect(record[label]); + return isNaN(parseInt(label)) ? `${label}: ${value}` : value; + }) + .join(", "); + return props + ? `${record.constructor.name}(${props})` + : record.constructor.name; + } + + #list(list) { + if (list instanceof $Empty) { + return "[]"; + } + + let char_out = 'charlist.from_string("'; + let list_out = "["; + + let current = list; + while (current instanceof $NonEmpty) { + let element = current.head; + current = current.tail; + + if (list_out !== "[") { + list_out += ", "; + } + list_out += this.inspect(element); + + if (char_out) { + if ( + globalThis.Number.isInteger(element) && + element >= 32 && + element <= 126 + ) { + char_out += globalThis.String.fromCharCode(element); + } else { + char_out = null; + } + } + } + + if (char_out) { + return char_out + '")'; + } else { + return list_out + "]"; + } + } + + #string(str) { + let new_str = '"'; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + switch (char) { + case "\n": + new_str += "\\n"; + break; + case "\r": + new_str += "\\r"; + break; + case "\t": + new_str += "\\t"; + break; + case "\f": + new_str += "\\f"; + break; + case "\\": + new_str += "\\\\"; + break; + case '"': + new_str += '\\"'; + break; + default: + if (char < " " || (char > "~" && char < "\u{00A0}")) { + new_str += + "\\u{" + + char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") + + "}"; + } else { + new_str += char; + } + } + } + new_str += '"'; + return new_str; + } + + #utfCodepoint(codepoint) { + return `//utfcodepoint(${globalThis.String.fromCodePoint(codepoint.value)})`; + } + + #bit_array(bits) { + if (bits.bitSize === 0) { + return "<<>>"; + } + + let acc = "<<"; + + for (let i = 0; i < bits.byteSize - 1; i++) { + acc += bits.byteAt(i).toString(); + acc += ", "; + } + + if (bits.byteSize * 8 === bits.bitSize) { + acc += bits.byteAt(bits.byteSize - 1).toString(); + } else { + const trailingBitsCount = bits.bitSize % 8; + acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount); + acc += `:size(${trailingBitsCount})`; + } + + acc += ">>"; + return acc; + } +} + diff --git a/ui/javascript/prelude.mjs b/ui/javascript/prelude.mjs new file mode 100644 index 0000000..0f9f8d8 --- /dev/null +++ b/ui/javascript/prelude.mjs @@ -0,0 +1,1561 @@ +export class CustomType { + withFields(fields) { + let properties = Object.keys(this).map((label) => + label in fields ? fields[label] : this[label], + ); + return new this.constructor(...properties); + } +} + +export class List { + static fromArray(array, tail) { + let t = tail || new Empty(); + for (let i = array.length - 1; i >= 0; --i) { + t = new NonEmpty(array[i], t); + } + return t; + } + + [Symbol.iterator]() { + return new ListIterator(this); + } + + toArray() { + return [...this]; + } + + atLeastLength(desired) { + let current = this; + while (desired-- > 0 && current) current = current.tail; + return current !== undefined; + } + + hasLength(desired) { + let current = this; + while (desired-- > 0 && current) current = current.tail; + return desired === -1 && current instanceof Empty; + } + + countLength() { + let current = this; + let length = 0; + while (current) { + current = current.tail; + length++; + } + return length - 1; + } +} + +export function prepend(element, tail) { + return new NonEmpty(element, tail); +} + +export function toList(elements, tail) { + return List.fromArray(elements, tail); +} + +class ListIterator { + #current; + + constructor(current) { + this.#current = current; + } + + next() { + if (this.#current instanceof Empty) { + return { done: true }; + } else { + let { head, tail } = this.#current; + this.#current = tail; + return { value: head, done: false }; + } + } +} + +export class Empty extends List {} +export const List$Empty = () => new Empty(); +export const List$isEmpty = (value) => value instanceof Empty; + +export class NonEmpty extends List { + constructor(head, tail) { + super(); + this.head = head; + this.tail = tail; + } +} +export const List$NonEmpty = (head, tail) => new NonEmpty(head, tail); +export const List$isNonEmpty = (value) => value instanceof NonEmpty; + +export const List$NonEmpty$first = (value) => value.head; +export const List$NonEmpty$rest = (value) => value.tail; + +/** + * A bit array is a contiguous sequence of bits similar to Erlang's Binary type. + */ +export class BitArray { + /** + * The size in bits of this bit array's data. + * + * @type {number} + */ + bitSize; + + /** + * The size in bytes of this bit array's data. If this bit array doesn't store + * a whole number of bytes then this value is rounded up. + * + * @type {number} + */ + byteSize; + + /** + * The number of unused high bits in the first byte of this bit array's + * buffer prior to the start of its data. The value of any unused high bits is + * undefined. + * + * The bit offset will be in the range 0-7. + * + * @type {number} + */ + bitOffset; + + /** + * The raw bytes that hold this bit array's data. + * + * If `bitOffset` is not zero then there are unused high bits in the first + * byte of this buffer. + * + * If `bitOffset + bitSize` is not a multiple of 8 then there are unused low + * bits in the last byte of this buffer. + * + * @type {Uint8Array} + */ + rawBuffer; + + /** + * Constructs a new bit array from a `Uint8Array`, an optional size in + * bits, and an optional bit offset. + * + * If no bit size is specified it is taken as `buffer.length * 8`, i.e. all + * bytes in the buffer make up the new bit array's data. + * + * If no bit offset is specified it defaults to zero, i.e. there are no unused + * high bits in the first byte of the buffer. + * + * @param {Uint8Array} buffer + * @param {number} [bitSize] + * @param {number} [bitOffset] + */ + constructor(buffer, bitSize, bitOffset) { + if (!(buffer instanceof Uint8Array)) { + throw globalThis.Error( + "BitArray can only be constructed from a Uint8Array", + ); + } + + this.bitSize = bitSize ?? buffer.length * 8; + this.byteSize = Math.trunc((this.bitSize + 7) / 8); + this.bitOffset = bitOffset ?? 0; + + // Validate the bit size + if (this.bitSize < 0) { + throw globalThis.Error(`BitArray bit size is invalid: ${this.bitSize}`); + } + + // Validate the bit offset + if (this.bitOffset < 0 || this.bitOffset > 7) { + throw globalThis.Error( + `BitArray bit offset is invalid: ${this.bitOffset}`, + ); + } + + // Validate the length of the buffer + if (buffer.length !== Math.trunc((this.bitOffset + this.bitSize + 7) / 8)) { + throw globalThis.Error("BitArray buffer length is invalid"); + } + + this.rawBuffer = buffer; + } + + /** + * Returns a specific byte in this bit array. If the byte index is out of + * range then `undefined` is returned. + * + * When returning the final byte of a bit array with a bit size that's not a + * multiple of 8, the content of the unused low bits are undefined. + * + * @param {number} index + * @returns {number | undefined} + */ + byteAt(index) { + if (index < 0 || index >= this.byteSize) { + return undefined; + } + + return bitArrayByteAt(this.rawBuffer, this.bitOffset, index); + } + + equals(other) { + if (this.bitSize !== other.bitSize) { + return false; + } + + const wholeByteCount = Math.trunc(this.bitSize / 8); + + // If both bit offsets are zero do a byte-aligned equality check which is + // faster + if (this.bitOffset === 0 && other.bitOffset === 0) { + // Compare any whole bytes + for (let i = 0; i < wholeByteCount; i++) { + if (this.rawBuffer[i] !== other.rawBuffer[i]) { + return false; + } + } + + // Compare any trailing bits, excluding unused low bits + const trailingBitsCount = this.bitSize % 8; + if (trailingBitsCount) { + const unusedLowBitCount = 8 - trailingBitsCount; + if ( + this.rawBuffer[wholeByteCount] >> unusedLowBitCount !== + other.rawBuffer[wholeByteCount] >> unusedLowBitCount + ) { + return false; + } + } + } else { + // Compare any whole bytes + for (let i = 0; i < wholeByteCount; i++) { + const a = bitArrayByteAt(this.rawBuffer, this.bitOffset, i); + const b = bitArrayByteAt(other.rawBuffer, other.bitOffset, i); + + if (a !== b) { + return false; + } + } + + // Compare any trailing bits + const trailingBitsCount = this.bitSize % 8; + if (trailingBitsCount) { + const a = bitArrayByteAt( + this.rawBuffer, + this.bitOffset, + wholeByteCount, + ); + const b = bitArrayByteAt( + other.rawBuffer, + other.bitOffset, + wholeByteCount, + ); + + const unusedLowBitCount = 8 - trailingBitsCount; + if (a >> unusedLowBitCount !== b >> unusedLowBitCount) { + return false; + } + } + } + + return true; + } + + /** + * Returns this bit array's internal buffer. + * + * @deprecated + * + * @returns {Uint8Array} + */ + get buffer() { + if (this.bitOffset !== 0 || this.bitSize % 8 !== 0) { + throw new globalThis.Error( + "BitArray.buffer does not support unaligned bit arrays", + ); + } + + return this.rawBuffer; + } + + /** + * Returns the length in bytes of this bit array's internal buffer. + * + * @deprecated + * + * @returns {number} + */ + get length() { + if (this.bitOffset !== 0 || this.bitSize % 8 !== 0) { + throw new globalThis.Error( + "BitArray.length does not support unaligned bit arrays", + ); + } + + return this.rawBuffer.length; + } +} + +export const BitArray$BitArray = (buffer, bitSize, bitOffset) => + new BitArray(buffer, bitSize, bitOffset); +export const BitArray$isBitArray = (value) => value instanceof BitArray; +export const BitArray$BitArray$data = (bitArray) => { + if (bitArray.bitSize % 8 !== 0) + throw new globalThis.Error( + "BitArray$BitArray$data called on un-aligned bit array", + ); + const array = bitArray.rawBuffer; + return new DataView(array.buffer, array.byteOffset, bitArray.byteSize); +}; + +/** + * Returns the nth byte in the given buffer, after applying the specified bit + * offset. If the index is out of bounds then zero is returned. + * + * @param {Uint8Array} buffer + * @param {number} bitOffset + * @param {number} index + * @returns {number} + */ +function bitArrayByteAt(buffer, bitOffset, index) { + if (bitOffset === 0) { + return buffer[index] ?? 0; + } else { + const a = (buffer[index] << bitOffset) & 0xff; + const b = buffer[index + 1] >> (8 - bitOffset); + + return a | b; + } +} + +export class UtfCodepoint { + constructor(value) { + this.value = value; + } +} + +/** + * Slices a bit array to produce a new bit array. If `end` is not supplied then + * all bits from `start` onward are returned. + * + * If the slice is out of bounds then an exception is thrown. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} [end] + * @returns {BitArray} + */ +export function bitArraySlice(bitArray, start, end) { + end ??= bitArray.bitSize; + + bitArrayValidateRange(bitArray, start, end); + + // Handle zero-length slices + if (start === end) { + return new BitArray(new Uint8Array()); + } + + // Early return for slices that cover the whole bit array + if (start === 0 && end === bitArray.bitSize) { + return bitArray; + } + + start += bitArray.bitOffset; + end += bitArray.bitOffset; + + const startByteIndex = Math.trunc(start / 8); + const endByteIndex = Math.trunc((end + 7) / 8); + const byteLength = endByteIndex - startByteIndex; + + // Avoid creating a new Uint8Array if the view of the underlying ArrayBuffer + // is the same. This can occur when slicing off just the first or last bit of + // a bit array, i.e. when only the bit offset or bit size need to be updated. + let buffer; + if (startByteIndex === 0 && byteLength === bitArray.rawBuffer.byteLength) { + buffer = bitArray.rawBuffer; + } else { + buffer = new Uint8Array( + bitArray.rawBuffer.buffer, + bitArray.rawBuffer.byteOffset + startByteIndex, + byteLength, + ); + } + + return new BitArray(buffer, end - start, start % 8); +} + +/** + * Interprets a slice of this bit array as a floating point number, either + * 32-bit or 64-bit, with the specified endianness. + * + * The value of `end - start` must be exactly 32 or 64, otherwise an exception + * will be thrown. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @returns {number} + */ +export function bitArraySliceToFloat(bitArray, start, end, isBigEndian) { + bitArrayValidateRange(bitArray, start, end); + + const floatSize = end - start; + + // Check size is valid + if (floatSize !== 16 && floatSize !== 32 && floatSize !== 64) { + const msg = + `Sized floats must be 16-bit, 32-bit or 64-bit, got size of ` + + `${floatSize} bits`; + throw new globalThis.Error(msg); + } + + start += bitArray.bitOffset; + + const isStartByteAligned = start % 8 === 0; + + // If the bit range is byte aligned then the float can be read directly out + // of the existing buffer + if (isStartByteAligned) { + const view = new DataView( + bitArray.rawBuffer.buffer, + bitArray.rawBuffer.byteOffset + start / 8, + ); + + if (floatSize === 64) { + return view.getFloat64(0, !isBigEndian); + } else if (floatSize === 32) { + return view.getFloat32(0, !isBigEndian); + } else if (floatSize === 16) { + return fp16UintToNumber(view.getUint16(0, !isBigEndian)); + } + } + + // Copy the unaligned bytes into an aligned array so a DataView can be used + const alignedBytes = new Uint8Array(floatSize / 8); + const byteOffset = Math.trunc(start / 8); + for (let i = 0; i < alignedBytes.length; i++) { + alignedBytes[i] = bitArrayByteAt( + bitArray.rawBuffer, + start % 8, + byteOffset + i, + ); + } + + // Read the float out of the aligned buffer + const view = new DataView(alignedBytes.buffer); + if (floatSize === 64) { + return view.getFloat64(0, !isBigEndian); + } else if (floatSize === 32) { + return view.getFloat32(0, !isBigEndian); + } else { + return fp16UintToNumber(view.getUint16(0, !isBigEndian)); + } +} + +/** + * Interprets a slice of this bit array as a signed or unsigned integer with the + * specified endianness. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +export function bitArraySliceToInt( + bitArray, + start, + end, + isBigEndian, + isSigned, +) { + bitArrayValidateRange(bitArray, start, end); + + if (start === end) { + return 0; + } + + start += bitArray.bitOffset; + end += bitArray.bitOffset; + + const isStartByteAligned = start % 8 === 0; + const isEndByteAligned = end % 8 === 0; + + // If the slice is byte-aligned then there is no need to handle unaligned + // slices, meaning a simpler and faster implementation can be used instead + if (isStartByteAligned && isEndByteAligned) { + return intFromAlignedSlice( + bitArray, + start / 8, + end / 8, + isBigEndian, + isSigned, + ); + } + + const size = end - start; + + const startByteIndex = Math.trunc(start / 8); + const endByteIndex = Math.trunc((end - 1) / 8); + + // Handle the case of the slice being completely contained in a single byte + if (startByteIndex == endByteIndex) { + const mask = 0xff >> (start % 8); + const unusedLowBitCount = (8 - (end % 8)) % 8; + + let value = + (bitArray.rawBuffer[startByteIndex] & mask) >> unusedLowBitCount; + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2 ** (size - 1); + if (value >= highBit) { + value -= highBit * 2; + } + } + + return value; + } + + // The integer value to be read is not aligned and crosses at least one byte + // boundary in the input array + + if (size <= 53) { + return intFromUnalignedSliceUsingNumber( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } else { + return intFromUnalignedSliceUsingBigInt( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } +} + +/** + * Joins the given segments into a new bit array, tightly packing them together. + * Each segment must be one of the following types: + * + * - A `number`: A single byte value in the range 0-255. Values outside this + * range will be wrapped. + * - A `Uint8Array`: A sequence of byte values of any length. + * - A `BitArray`: A sequence of bits of any length, which may not be byte + * aligned. + * + * The bit size of the returned bit array will be the sum of the size in bits + * of the input segments. + * + * @param {(number | Uint8Array | BitArray)[]} segments + * @returns {BitArray} + */ +export function toBitArray(segments) { + if (segments.length === 0) { + return new BitArray(new Uint8Array()); + } + + if (segments.length === 1) { + const segment = segments[0]; + + // When there is a single BitArray segment it can be returned as-is + if (segment instanceof BitArray) { + return segment; + } + + // When there is a single Uint8Array segment, pass it directly to the bit + // array constructor to avoid a copy + if (segment instanceof Uint8Array) { + return new BitArray(segment); + } + + return new BitArray(new Uint8Array(/** @type {number[]} */ (segments))); + } + + // Count the total number of bits and check if all segments are numbers, i.e. + // single bytes + let bitSize = 0; + let areAllSegmentsNumbers = true; + for (const segment of segments) { + if (segment instanceof BitArray) { + bitSize += segment.bitSize; + areAllSegmentsNumbers = false; + } else if (segment instanceof Uint8Array) { + bitSize += segment.byteLength * 8; + areAllSegmentsNumbers = false; + } else { + bitSize += 8; + } + } + + // If all segments are numbers then pass the segments array directly to the + // Uint8Array constructor + if (areAllSegmentsNumbers) { + return new BitArray(new Uint8Array(/** @type {number[]} */ (segments))); + } + + // Pack the segments into a Uint8Array + const buffer = new Uint8Array(Math.trunc((bitSize + 7) / 8)); + + // The current write position in bits into the above array. Byte-aligned + // segments, i.e. when the cursor is a multiple of 8, are able to be processed + // faster due to being able to copy bytes directly. + let cursor = 0; + + for (let segment of segments) { + const isCursorByteAligned = cursor % 8 === 0; + + if (segment instanceof BitArray) { + if (isCursorByteAligned && segment.bitOffset === 0) { + buffer.set(segment.rawBuffer, cursor / 8); + cursor += segment.bitSize; + + // Zero any unused bits in the last byte of the buffer. Their content is + // undefined and shouldn't be included in the output. + const trailingBitsCount = segment.bitSize % 8; + if (trailingBitsCount !== 0) { + const lastByteIndex = Math.trunc(cursor / 8); + buffer[lastByteIndex] >>= 8 - trailingBitsCount; + buffer[lastByteIndex] <<= 8 - trailingBitsCount; + } + } else { + appendUnalignedBits( + segment.rawBuffer, + segment.bitSize, + segment.bitOffset, + ); + } + } else if (segment instanceof Uint8Array) { + if (isCursorByteAligned) { + buffer.set(segment, cursor / 8); + cursor += segment.byteLength * 8; + } else { + appendUnalignedBits(segment, segment.byteLength * 8, 0); + } + } else { + if (isCursorByteAligned) { + buffer[cursor / 8] = segment; + cursor += 8; + } else { + appendUnalignedBits(new Uint8Array([segment]), 8, 0); + } + } + } + + function appendUnalignedBits(unalignedBits, size, offset) { + if (size === 0) { + return; + } + + const byteSize = Math.trunc(size + 7 / 8); + + const highBitsCount = cursor % 8; + const lowBitsCount = 8 - highBitsCount; + + let byteIndex = Math.trunc(cursor / 8); + + for (let i = 0; i < byteSize; i++) { + let byte = bitArrayByteAt(unalignedBits, offset, i); + + // If this is a partial byte then zero out the trailing bits as their + // content is undefined and shouldn't be included in the output + if (size < 8) { + byte >>= 8 - size; + byte <<= 8 - size; + } + + // Copy the high bits of the input byte to the low bits of the current + // output byte + buffer[byteIndex] |= byte >> highBitsCount; + + let appendedBitsCount = size - Math.max(0, size - lowBitsCount); + size -= appendedBitsCount; + cursor += appendedBitsCount; + + if (size === 0) { + break; + } + + // Copy the low bits of the input byte to the high bits of the next output + // byte + buffer[++byteIndex] = byte << lowBitsCount; + appendedBitsCount = size - Math.max(0, size - highBitsCount); + size -= appendedBitsCount; + cursor += appendedBitsCount; + } + } + + return new BitArray(buffer, bitSize); +} + +/** + * Encodes a floating point value into a `Uint8Array`. This is used to create + * float segments that are part of bit array expressions. + * + * @param {number} value + * @param {number} size + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function sizedFloat(value, size, isBigEndian) { + if (size !== 16 && size !== 32 && size !== 64) { + const msg = `Sized floats must be 16-bit, 32-bit or 64-bit, got size of ${size} bits`; + throw new globalThis.Error(msg); + } + + if (size === 16) { + return numberToFp16Uint(value, isBigEndian); + } + + const buffer = new Uint8Array(size / 8); + + const view = new DataView(buffer.buffer); + + if (size == 64) { + view.setFloat64(0, value, !isBigEndian); + } else { + view.setFloat32(0, value, !isBigEndian); + } + + return buffer; +} + +/** + * Encodes an integer value into a `Uint8Array`, or a `BitArray` if the size in + * bits is not a multiple of 8. This is used to create integer segments used in + * bit array expressions. + * + * @param {number} value + * @param {number} size + * @param {boolean} isBigEndian + * @returns {Uint8Array | BitArray} + */ +export function sizedInt(value, size, isBigEndian) { + if (size <= 0) { + return new Uint8Array(); + } + + // Fast path when size is 8 bits. This relies on the rounding behavior of the + // Uint8Array constructor. + if (size === 8) { + return new Uint8Array([value]); + } + + // Fast path when size is less than 8 bits: shift the value up to the high + // bits + if (size < 8) { + value <<= 8 - size; + return new BitArray(new Uint8Array([value]), size); + } + + // Allocate output buffer + const buffer = new Uint8Array(Math.trunc((size + 7) / 8)); + + // The number of trailing bits in the final byte. Will be zero if the size is + // an exact number of bytes. + const trailingBitsCount = size % 8; + + // The number of unused bits in the final byte of the buffer + const unusedBitsCount = 8 - trailingBitsCount; + + // For output sizes not exceeding 32 bits the number type is used. For larger + // output sizes the BigInt type is needed. + // + // The code in each of these two paths must be kept in sync. + if (size <= 32) { + if (isBigEndian) { + let i = buffer.length - 1; + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i--] = (value << unusedBitsCount) & 0xff; + value >>= trailingBitsCount; + } + + for (; i >= 0; i--) { + buffer[i] = value; + value >>= 8; + } + } else { + let i = 0; + + const wholeByteCount = Math.trunc(size / 8); + for (; i < wholeByteCount; i++) { + buffer[i] = value; + value >>= 8; + } + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i] = value << unusedBitsCount; + } + } + } else { + const bigTrailingBitsCount = BigInt(trailingBitsCount); + const bigUnusedBitsCount = BigInt(unusedBitsCount); + + let bigValue = BigInt(value); + + if (isBigEndian) { + let i = buffer.length - 1; + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i--] = Number(bigValue << bigUnusedBitsCount); + bigValue >>= bigTrailingBitsCount; + } + + for (; i >= 0; i--) { + buffer[i] = Number(bigValue); + bigValue >>= 8n; + } + } else { + let i = 0; + + const wholeByteCount = Math.trunc(size / 8); + for (; i < wholeByteCount; i++) { + buffer[i] = Number(bigValue); + bigValue >>= 8n; + } + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i] = Number(bigValue << bigUnusedBitsCount); + } + } + } + + // Integers that aren't a whole number of bytes are returned as a BitArray so + // their size in bits is tracked + if (trailingBitsCount) { + return new BitArray(buffer, size); + } + + return buffer; +} + +/** + * Reads an aligned slice of any size as an integer. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromAlignedSlice(bitArray, start, end, isBigEndian, isSigned) { + const byteSize = end - start; + + if (byteSize <= 6) { + return intFromAlignedSliceUsingNumber( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } else { + return intFromAlignedSliceUsingBigInt( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } +} + +/** + * Reads an aligned slice up to 48 bits in size as an integer. Uses the + * JavaScript `number` type internally. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromAlignedSliceUsingNumber( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const byteSize = end - start; + + let value = 0; + + // Read bytes as an unsigned integer + if (isBigEndian) { + for (let i = start; i < end; i++) { + value *= 256; + value += buffer[i]; + } + } else { + for (let i = end - 1; i >= start; i--) { + value *= 256; + value += buffer[i]; + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2 ** (byteSize * 8 - 1); + if (value >= highBit) { + value -= highBit * 2; + } + } + + return value; +} + +/** + * Reads an aligned slice of any size as an integer. Uses the JavaScript + * `BigInt` type internally. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromAlignedSliceUsingBigInt( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const byteSize = end - start; + + let value = 0n; + + // Read bytes as an unsigned integer value + if (isBigEndian) { + for (let i = start; i < end; i++) { + value *= 256n; + value += BigInt(buffer[i]); + } + } else { + for (let i = end - 1; i >= start; i--) { + value *= 256n; + value += BigInt(buffer[i]); + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 1n << BigInt(byteSize * 8 - 1); + if (value >= highBit) { + value -= highBit * 2n; + } + } + + // Convert the result into a JS number. This may cause quantizing/error on + // values outside JavaScript's safe integer range. + return Number(value); +} + +/** + * Reads an unaligned slice up to 53 bits in size as an integer. Uses the + * JavaScript `number` type internally. + * + * This function assumes that the slice crosses at least one byte boundary in + * the input. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromUnalignedSliceUsingNumber( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const isStartByteAligned = start % 8 === 0; + + let size = end - start; + let byteIndex = Math.trunc(start / 8); + + let value = 0; + + if (isBigEndian) { + // Read any leading bits + if (!isStartByteAligned) { + const leadingBitsCount = 8 - (start % 8); + value = buffer[byteIndex++] & ((1 << leadingBitsCount) - 1); + size -= leadingBitsCount; + } + + // Read any whole bytes + while (size >= 8) { + value *= 256; + value += buffer[byteIndex++]; + size -= 8; + } + + // Read any trailing bits + if (size > 0) { + value *= 2 ** size; + value += buffer[byteIndex] >> (8 - size); + } + } else { + // For little endian, if the start is aligned then whole bytes can be read + // directly out of the input array, with the trailing bits handled at the + // end + if (isStartByteAligned) { + let size = end - start; + let scale = 1; + + // Read whole bytes + while (size >= 8) { + value += buffer[byteIndex++] * scale; + scale *= 256; + size -= 8; + } + + // Read trailing bits + value += (buffer[byteIndex] >> (8 - size)) * scale; + } else { + // Read little endian data where the start is not byte-aligned. This is + // done by reading whole bytes that cross a byte boundary in the input + // data, then reading any trailing bits. + + const highBitsCount = start % 8; + const lowBitsCount = 8 - highBitsCount; + + let size = end - start; + let scale = 1; + + // Extract whole bytes + while (size >= 8) { + const byte = + (buffer[byteIndex] << highBitsCount) | + (buffer[byteIndex + 1] >> lowBitsCount); + + value += (byte & 0xff) * scale; + + scale *= 256; + size -= 8; + byteIndex++; + } + + // Read any trailing bits. These trailing bits may cross a byte boundary + // in the input buffer. + if (size > 0) { + const lowBitsUsed = size - Math.max(0, size - lowBitsCount); + + let trailingByte = + (buffer[byteIndex] & ((1 << lowBitsCount) - 1)) >> + (lowBitsCount - lowBitsUsed); + + size -= lowBitsUsed; + + if (size > 0) { + trailingByte *= 2 ** size; + trailingByte += buffer[byteIndex + 1] >> (8 - size); + } + + value += trailingByte * scale; + } + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2 ** (end - start - 1); + if (value >= highBit) { + value -= highBit * 2; + } + } + + return value; +} + +/** + * Reads an unaligned slice of any size as an integer. Uses the JavaScript + * `BigInt` type internally. + * + * This function assumes that the slice crosses at least one byte boundary in + * the input. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromUnalignedSliceUsingBigInt( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const isStartByteAligned = start % 8 === 0; + + let size = end - start; + let byteIndex = Math.trunc(start / 8); + + let value = 0n; + + if (isBigEndian) { + // Read any leading bits + if (!isStartByteAligned) { + const leadingBitsCount = 8 - (start % 8); + value = BigInt(buffer[byteIndex++] & ((1 << leadingBitsCount) - 1)); + size -= leadingBitsCount; + } + + // Read any whole bytes + while (size >= 8) { + value *= 256n; + value += BigInt(buffer[byteIndex++]); + size -= 8; + } + + // Read any trailing bits + if (size > 0) { + value <<= BigInt(size); + value += BigInt(buffer[byteIndex] >> (8 - size)); + } + } else { + // For little endian, if the start is aligned then whole bytes can be read + // directly out of the input array, with the trailing bits handled at the + // end + if (isStartByteAligned) { + let size = end - start; + let shift = 0n; + + // Read whole bytes + while (size >= 8) { + value += BigInt(buffer[byteIndex++]) << shift; + shift += 8n; + size -= 8; + } + + // Read trailing bits + value += BigInt(buffer[byteIndex] >> (8 - size)) << shift; + } else { + // Read little endian data where the start is not byte-aligned. This is + // done by reading whole bytes that cross a byte boundary in the input + // data, then reading any trailing bits. + + const highBitsCount = start % 8; + const lowBitsCount = 8 - highBitsCount; + + let size = end - start; + let shift = 0n; + + // Extract whole bytes + while (size >= 8) { + const byte = + (buffer[byteIndex] << highBitsCount) | + (buffer[byteIndex + 1] >> lowBitsCount); + + value += BigInt(byte & 0xff) << shift; + + shift += 8n; + size -= 8; + byteIndex++; + } + + // Read any trailing bits. These trailing bits may cross a byte boundary + // in the input buffer. + if (size > 0) { + const lowBitsUsed = size - Math.max(0, size - lowBitsCount); + + let trailingByte = + (buffer[byteIndex] & ((1 << lowBitsCount) - 1)) >> + (lowBitsCount - lowBitsUsed); + + size -= lowBitsUsed; + + if (size > 0) { + trailingByte <<= size; + trailingByte += buffer[byteIndex + 1] >> (8 - size); + } + + value += BigInt(trailingByte) << shift; + } + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2n ** BigInt(end - start - 1); + if (value >= highBit) { + value -= highBit * 2n; + } + } + + // Convert the result into a JS number. This may cause quantizing/error on + // values outside JavaScript's safe integer range. + return Number(value); +} + +/** + * Interprets a 16-bit unsigned integer value as a 16-bit floating point value. + * + * @param {number} intValue + * @returns {number} + */ +function fp16UintToNumber(intValue) { + const sign = intValue >= 0x8000 ? -1 : 1; + const exponent = (intValue & 0x7c00) >> 10; + const fraction = intValue & 0x03ff; + + let value; + if (exponent === 0) { + value = 6.103515625e-5 * (fraction / 0x400); + } else if (exponent === 0x1f) { + value = fraction === 0 ? Infinity : NaN; + } else { + value = Math.pow(2, exponent - 15) * (1 + fraction / 0x400); + } + + return sign * value; +} + +/** + * Converts a floating point number to bytes for a 16-bit floating point value. + * + * @param {number} intValue + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +function numberToFp16Uint(value, isBigEndian) { + const buffer = new Uint8Array(2); + + if (isNaN(value)) { + buffer[1] = 0x7e; + } else if (value === Infinity) { + buffer[1] = 0x7c; + } else if (value === -Infinity) { + buffer[1] = 0xfc; + } else if (value === 0) { + // Both values are already zero + } else { + const sign = value < 0 ? 1 : 0; + value = Math.abs(value); + + let exponent = Math.floor(Math.log2(value)); + let fraction = value / Math.pow(2, exponent) - 1; + + exponent += 15; + + if (exponent <= 0) { + exponent = 0; + fraction = value / Math.pow(2, -14); + } else if (exponent >= 31) { + exponent = 31; + fraction = 0; + } + + fraction = Math.round(fraction * 1024); + + buffer[1] = + (sign << 7) | ((exponent & 0x1f) << 2) | ((fraction >> 8) & 0x03); + buffer[0] = fraction & 0xff; + } + + if (isBigEndian) { + const a = buffer[0]; + buffer[0] = buffer[1]; + buffer[1] = a; + } + + return buffer; +} + +/** + * Throws an exception if the given start and end values are out of bounds for + * a bit array. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + */ +function bitArrayValidateRange(bitArray, start, end) { + if ( + start < 0 || + start > bitArray.bitSize || + end < start || + end > bitArray.bitSize + ) { + const msg = + `Invalid bit array slice: start = ${start}, end = ${end}, ` + + `bit size = ${bitArray.bitSize}`; + throw new globalThis.Error(msg); + } +} + +/** @type {TextEncoder | undefined} */ +let utf8Encoder; + +/** + * Returns the UTF-8 bytes for a string. + * + * @param {string} string + * @returns {Uint8Array} + */ +export function stringBits(string) { + utf8Encoder ??= new TextEncoder(); + return utf8Encoder.encode(string); +} + +/** + * Returns the UTF-8 bytes for a single UTF codepoint. + * + * @param {UtfCodepoint} codepoint + * @returns {Uint8Array} + */ +export function codepointBits(codepoint) { + return stringBits(String.fromCodePoint(codepoint.value)); +} + +/** + * Returns the UTF-16 bytes for a string. + * + * @param {string} string + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function stringToUtf16(string, isBigEndian) { + const buffer = new ArrayBuffer(string.length * 2); + const bufferView = new DataView(buffer); + + for (let i = 0; i < string.length; i++) { + bufferView.setUint16(i * 2, string.charCodeAt(i), !isBigEndian); + } + + return new Uint8Array(buffer); +} + +/** + * Returns the UTF-16 bytes for a single UTF codepoint. + * + * @param {UtfCodepoint} codepoint + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function codepointToUtf16(codepoint, isBigEndian) { + return stringToUtf16(String.fromCodePoint(codepoint.value), isBigEndian); +} + +/** + * Returns the UTF-32 bytes for a string. + * + * @param {string} string + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function stringToUtf32(string, isBigEndian) { + const buffer = new ArrayBuffer(string.length * 4); + const bufferView = new DataView(buffer); + let length = 0; + + for (let i = 0; i < string.length; i++) { + const codepoint = string.codePointAt(i); + + bufferView.setUint32(length * 4, codepoint, !isBigEndian); + length++; + + if (codepoint > 0xffff) { + i++; + } + } + + return new Uint8Array(buffer.slice(0, length * 4)); +} + +/** + * Returns the UTF-32 bytes for a single UTF codepoint. + * + * @param {UtfCodepoint} codepoint + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function codepointToUtf32(codepoint, isBigEndian) { + return stringToUtf32(String.fromCodePoint(codepoint.value), isBigEndian); +} + +export class Result extends CustomType { + static isResult(data) { + return data instanceof Result; + } +} + +export class Ok extends Result { + constructor(value) { + super(); + this[0] = value; + } + + isOk() { + return true; + } +} +export const Result$Ok = (value) => new Ok(value); +export const Result$isOk = (value) => value instanceof Ok; +export const Result$Ok$0 = (value) => value[0]; + +export class Error extends Result { + constructor(detail) { + super(); + this[0] = detail; + } + + isOk() { + return false; + } +} +export const Result$Error = (detail) => new Error(detail); +export const Result$isError = (value) => value instanceof Error; +export const Result$Error$0 = (value) => value[0]; + +export function isEqual(x, y) { + let values = [x, y]; + + while (values.length) { + let a = values.pop(); + let b = values.pop(); + if (a === b) continue; + + if (!isObject(a) || !isObject(b)) return false; + let unequal = + !structurallyCompatibleObjects(a, b) || + unequalDates(a, b) || + unequalBuffers(a, b) || + unequalArrays(a, b) || + unequalMaps(a, b) || + unequalSets(a, b) || + unequalRegExps(a, b); + if (unequal) return false; + + const proto = Object.getPrototypeOf(a); + if (proto !== null && typeof proto.equals === "function") { + try { + if (a.equals(b)) continue; + else return false; + } catch {} + } + + let [keys, get] = getters(a); + const ka = keys(a); + const kb = keys(b); + if (ka.length !== kb.length) return false; + for (let k of ka) { + values.push(get(a, k), get(b, k)); + } + } + + return true; +} + +function getters(object) { + if (object instanceof Map) { + return [(x) => x.keys(), (x, y) => x.get(y)]; + } else { + let extra = object instanceof globalThis.Error ? ["message"] : []; + return [(x) => [...extra, ...Object.keys(x)], (x, y) => x[y]]; + } +} + +function unequalDates(a, b) { + return a instanceof Date && (a > b || a < b); +} + +function unequalBuffers(a, b) { + return ( + !(a instanceof BitArray) && + a.buffer instanceof ArrayBuffer && + a.BYTES_PER_ELEMENT && + !(a.byteLength === b.byteLength && a.every((n, i) => n === b[i])) + ); +} + +function unequalArrays(a, b) { + return Array.isArray(a) && a.length !== b.length; +} + +function unequalMaps(a, b) { + return a instanceof Map && a.size !== b.size; +} + +function unequalSets(a, b) { + return ( + a instanceof Set && (a.size != b.size || [...a].some((e) => !b.has(e))) + ); +} + +function unequalRegExps(a, b) { + return a instanceof RegExp && (a.source !== b.source || a.flags !== b.flags); +} + +function isObject(a) { + return typeof a === "object" && a !== null; +} + +function structurallyCompatibleObjects(a, b) { + if (typeof a !== "object" && typeof b !== "object" && (!a || !b)) + return false; + + let nonstructural = [Promise, WeakSet, WeakMap, Function]; + if (nonstructural.some((c) => a instanceof c)) return false; + + return a.constructor === b.constructor; +} + +export function remainderInt(a, b) { + if (b === 0) { + return 0; + } else { + return a % b; + } +} + +export function divideInt(a, b) { + return Math.trunc(divideFloat(a, b)); +} + +export function divideFloat(a, b) { + if (b === 0) { + return 0; + } else { + return a / b; + } +} + +export function makeError(variant, file, module, line, fn, message, extra) { + let error = new globalThis.Error(message); + error.gleam_error = variant; + error.file = file; + error.module = module; + error.line = line; + error.function = fn; + // TODO: Remove this with Gleam v2.0.0 + error.fn = fn; + for (let k in extra) error[k] = extra[k]; + return error; +} diff --git a/ui/javascript/simplifile/_gleam_artefacts/simplifile.cache b/ui/javascript/simplifile/_gleam_artefacts/simplifile.cache new file mode 100644 index 0000000000000000000000000000000000000000..2483b272ee92b88b2f960938cdd24ba495d9b1bf GIT binary patch literal 65770 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 Uy3ca|qII8n77NgTgwgAN0p0yHMF0Q* literal 0 HcmV?d00001 diff --git a/ui/javascript/simplifile/_gleam_artefacts/simplifile.cache_meta b/ui/javascript/simplifile/_gleam_artefacts/simplifile.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..0e4e97b1a700d8afc598b0e97b273ce421f206ed GIT binary patch literal 3248 zcmY+`dzj8u9suyeNEQhhmlT7rCS|uU#--eHC%0Ia(y$ZbZj4$*7q{ItGhPu|mtlFd zqLj4qEVW~oMGU*-7LCg=qaCXi(iF9ycg;VY=lMMI{?6}x&+q)s_xn9(7SDM%B^0+3 zcNTvrQ~dY2l(ESZlhQ^-FYhiE{XBAPa?-da!&B0RC8ee&O|7V_0Y8i0({A5R8K2hB zMFM4`z1r;?V^b!lwQ$vy^3iVX_MOz^$y3Irb#whRKOJsza@sRGr}EPuO-@Tq89(wx z{h2iVvFN3KNoU`L+WD7B+LTE)tMlgn%WUzuY?Bg?TamO7Y#LB4ZvGlxaI$5u{|M-e4Un-BGAPY=fNJQ3dHBeFS39ybW`A&y3LqBkR%#C(>rjlXk= zBI4yvb2{@R!x_yC(pks*>?DWtT%x>a$MPV}=*0lCSjRE`%hy~bUQQ*_iC6g@8Dw*i zZ-^^rop_EpY~TPzG>}9+nLs)paF9aE-xb1R4C7^1lEW1$RS2O814-i#Y~?W5sIN|X zF_rZk<_7mm!nUNajN@FVW+gEpnHg;543#Qd52{g}K1A5b-S>o0k0x|yFj>6CSCp$F zR&3%5kH&^Dh3V`frmFZbgwd>G6Q!z!P=!RgGl^Mzzz=k-9>Nliae?e;O2~CVoUxx89yV%Q35`JNg7|v7{v6v&&_@y1>Po)x~;7t6Ns>oV^nApLK{XgmhntrGexv{OdQz8Y0h$v z>W|A)7O|M~d`V1O@5fxu(72uY|y$^IV{6hY+4+BYQYY9_K0D z(fPzIX0w%&Plz82$f0H@XE&{xzD75lhFnXc-V{>)|$ ze<7F4#CHoJiAgLbi_PRyySsQXfGj@YAo)z~;T>4WSqiwsH+)MG2|d*)tND=KT%c+% z^*{p}@*CE3nOaZkrw2oMhGA@CJK2=)EeDywCQfpS@2K9#{}UuJm-+mWwfu>7?Bz81 zT;^N8<9nL*b>=aM(M;zxW-^N%?6z<0zkVk?YhSY;KHZBm22T%n-Rm9Np+eguUcbd8qd%mX^F$ J%#;7${s%LwG3x*T literal 0 HcmV?d00001 diff --git a/ui/javascript/simplifile/gleam.mjs b/ui/javascript/simplifile/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/ui/javascript/simplifile/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/ui/javascript/simplifile/simplifile.erl b/ui/javascript/simplifile/simplifile.erl new file mode 100644 index 0000000..9e0596a --- /dev/null +++ b/ui/javascript/simplifile/simplifile.erl @@ -0,0 +1,903 @@ +-module(simplifile). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/simplifile.gleam"). +-export([describe_error/1, file_info_permissions_octal/1, file_info_type/1, file_info/1, link_info/1, delete/1, delete_file/1, delete_all/1, read_bits/1, read/1, write_bits/2, write/2, append_bits/2, append/2, is_directory/1, create_directory/1, create_symlink/2, create_link/2, read_directory/1, is_file/1, is_symlink/1, create_file/1, create_directory_all/1, copy_file/2, rename_file/2, rename/2, copy_directory/2, copy/2, rename_directory/2, clear_directory/1, get_files/1, file_permissions_to_octal/1, file_info_permissions/1, set_permissions_octal/2, set_permissions/2, current_directory/0]). +-export_type([file_error/0, file_info/0, file_type/0, permission/0, file_permissions/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 file_error() :: eacces | + eagain | + ebadf | + ebadmsg | + ebusy | + edeadlk | + edeadlock | + edquot | + eexist | + efault | + efbig | + eftype | + eintr | + einval | + eio | + eisdir | + eloop | + emfile | + emlink | + emultihop | + enametoolong | + enfile | + enobufs | + enodev | + enolck | + enolink | + enoent | + enomem | + enospc | + enosr | + enostr | + enosys | + enotblk | + enotdir | + enotsup | + enxio | + eopnotsupp | + eoverflow | + eperm | + epipe | + erange | + erofs | + espipe | + esrch | + estale | + etxtbsy | + exdev | + not_utf8 | + {unknown, binary()}. + +-type file_info() :: {file_info, + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer()}. + +-type file_type() :: file | directory | symlink | other. + +-type permission() :: read | write | execute. + +-type file_permissions() :: {file_permissions, + gleam@set:set(permission()), + gleam@set:set(permission()), + gleam@set:set(permission())}. + +-file("src/simplifile.gleam", 124). +?DOC( + " Convert an error into a human-readable description\n" + " ## Example\n" + " ```gleam\n" + " let assert \"Input/output error\" = describe_error(Eio)\n" + " ```\n" +). +-spec describe_error(file_error()) -> binary(). +describe_error(Error) -> + case Error of + eperm -> + <<"Operation not permitted"/utf8>>; + + enoent -> + <<"No such file or directory"/utf8>>; + + esrch -> + <<"No such process"/utf8>>; + + eintr -> + <<"Interrupted system call"/utf8>>; + + eio -> + <<"Input/output error"/utf8>>; + + enxio -> + <<"Device not configured"/utf8>>; + + ebadf -> + <<"Bad file descriptor"/utf8>>; + + edeadlk -> + <<"Resource deadlock avoided"/utf8>>; + + edeadlock -> + <<"Resource deadlock avoided"/utf8>>; + + enomem -> + <<"Cannot allocate memory"/utf8>>; + + eacces -> + <<"Permission denied"/utf8>>; + + efault -> + <<"Bad address"/utf8>>; + + enotblk -> + <<"Block device required"/utf8>>; + + ebusy -> + <<"Resource busy"/utf8>>; + + eexist -> + <<"File exists"/utf8>>; + + exdev -> + <<"Cross-device link"/utf8>>; + + enodev -> + <<"Operation not supported by device"/utf8>>; + + enotdir -> + <<"Not a directory"/utf8>>; + + eisdir -> + <<"Is a directory"/utf8>>; + + einval -> + <<"Invalid argument"/utf8>>; + + enfile -> + <<"Too many open files in system"/utf8>>; + + emfile -> + <<"Too many open files"/utf8>>; + + etxtbsy -> + <<"Text file busy"/utf8>>; + + efbig -> + <<"File too large"/utf8>>; + + enospc -> + <<"No space left on device"/utf8>>; + + espipe -> + <<"Illegal seek"/utf8>>; + + erofs -> + <<"Read-only file system"/utf8>>; + + emlink -> + <<"Too many links"/utf8>>; + + epipe -> + <<"Broken pipe"/utf8>>; + + erange -> + <<"Result too large"/utf8>>; + + eagain -> + <<"Resource temporarily unavailable"/utf8>>; + + enotsup -> + <<"Operation not supported"/utf8>>; + + enobufs -> + <<"No buffer space available"/utf8>>; + + eloop -> + <<"Too many levels of symbolic links"/utf8>>; + + enametoolong -> + <<"File name too long"/utf8>>; + + edquot -> + <<"Disc quota exceeded"/utf8>>; + + estale -> + <<"Stale NFS file handle"/utf8>>; + + enolck -> + <<"No locks available"/utf8>>; + + enosys -> + <<"Function not implemented"/utf8>>; + + eftype -> + <<"Inappropriate file type or format"/utf8>>; + + eoverflow -> + <<"Value too large to be stored in data type"/utf8>>; + + ebadmsg -> + <<"Bad message"/utf8>>; + + emultihop -> + <<"Multihop attempted"/utf8>>; + + enolink -> + <<"Link has been severed"/utf8>>; + + enosr -> + <<"No STREAM resources"/utf8>>; + + enostr -> + <<"Not a STREAM"/utf8>>; + + eopnotsupp -> + <<"Operation not supported on socket"/utf8>>; + + not_utf8 -> + <<"File not UTF-8 encoded"/utf8>>; + + {unknown, Inner} -> + <<"Unknown error: "/utf8, Inner/binary>> + end. + +-file("src/simplifile.gleam", 232). +?DOC( + " Extract the file permissions from a given FileInfo value in their octal representation.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " use info <- result.try(simplifile.file_info(\"src/app.gleam\"))\n" + " simplifile.file_info_permissions_octal(info)\n" + " // --> 0o644\n" + " ```\n" +). +-spec file_info_permissions_octal(file_info()) -> integer(). +file_info_permissions_octal(File_info) -> + erlang:'band'(erlang:element(3, File_info), 8#777). + +-file("src/simplifile.gleam", 261). +?DOC( + " Extract the file type from a given FileInfo value.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " use info <- result.try(simplifile.file_info(\"src/app.gleam\"))\n" + " simplifile.file_info_type(info)\n" + " // --> simplifile.File\n" + " ```\n" +). +-spec file_info_type(file_info()) -> file_type(). +file_info_type(File_info) -> + case erlang:'band'(erlang:element(3, File_info), 8#170000) of + 8#100000 -> + file; + + 8#40000 -> + directory; + + 8#120000 -> + symlink; + + _ -> + other + end. + +-file("src/simplifile.gleam", 281). +?DOC( + " Get information about a file at a given path\n" + "\n" + " When the given `filepath` points to a symlink, this function will follow\n" + " the symlink and return information about the target file.\n" + "\n" + " See `link_info` if you want to get information about a symlink instead.\n" +). +-spec file_info(binary()) -> {ok, file_info()} | {error, file_error()}. +file_info(Filepath) -> + simplifile_erl:file_info(Filepath). + +-file("src/simplifile.gleam", 291). +?DOC( + " Get information about a file at a given path\n" + "\n" + " When the given `filepath` is a symlink, this function will return\n" + " infromation about the symlink itself.\n" + "\n" + " See `file_info` if you want to follow symlinks instead.\n" +). +-spec link_info(binary()) -> {ok, file_info()} | {error, file_error()}. +link_info(Filepath) -> + simplifile_erl:link_info(Filepath). + +-file("src/simplifile.gleam", 335). +?DOC( + " Delete a file or directory at a given path. Performs a recursive\n" + " delete on a directory.\n" + " Throws an error if the path does not exist.\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(Nil) = delete(file_at: \"./delete_me.txt\")\n" + " ```\n" +). +-spec delete(binary()) -> {ok, nil} | {error, file_error()}. +delete(Path) -> + simplifile_erl:delete(Path). + +-file("src/simplifile.gleam", 342). +?DOC( + " Delete a file. \n" + " On Erlang, if you're specifically deleting a single file, this function \n" + " should be more efficient than `delete` because it can pass the raw option.\n" +). +-spec delete_file(binary()) -> {ok, nil} | {error, file_error()}. +delete_file(Path) -> + simplifile_erl:delete_file(Path). + +-file("src/simplifile.gleam", 349). +?DOC( + " Delete all files/directories specified in a list of paths.\n" + " Recursively deletes provided directories.\n" + " Does not return an error if one or more of the provided paths\n" + " do not exist.\n" +). +-spec delete_all(list(binary())) -> {ok, nil} | {error, file_error()}. +delete_all(Paths) -> + case Paths of + [] -> + {ok, nil}; + + [Path | Rest] -> + case simplifile_erl:delete(Path) of + {ok, nil} -> + delete_all(Rest); + + {error, enoent} -> + delete_all(Rest); + + E -> + E + end + end. + +-file("src/simplifile.gleam", 383). +?DOC( + " Read a files contents as a bitstring\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(records) = read_bits(from: \"./users.csv\")\n" + " ```\n" +). +-spec read_bits(binary()) -> {ok, bitstring()} | {error, file_error()}. +read_bits(Filepath) -> + simplifile_erl:read_bits(Filepath). + +-file("src/simplifile.gleam", 299). +?DOC( + " Read a files contents as a string\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(records) = read(from: \"./users.csv\")\n" + " ```\n" +). +-spec read(binary()) -> {ok, binary()} | {error, file_error()}. +read(Filepath) -> + case simplifile_erl:read_bits(Filepath) of + {ok, Bits} -> + case gleam@bit_array:to_string(Bits) of + {ok, Str} -> + {ok, Str}; + + _ -> + {error, not_utf8} + end; + + {error, E} -> + {error, E} + end. + +-file("src/simplifile.gleam", 393). +?DOC( + " Write a bitstring to a file at the given path\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(Nil) = write_bits(to: \"./hello_world.txt\", bits: <<\"Hello, World!\":utf8>>)\n" + " ```\n" +). +-spec write_bits(binary(), bitstring()) -> {ok, nil} | {error, file_error()}. +write_bits(Filepath, Bits) -> + simplifile_erl:write_bits(Filepath, Bits). + +-file("src/simplifile.gleam", 317). +?DOC( + " Write a string to a file at the given path\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(Nil) = write(to: \"./hello_world.txt\", contents: \"Hello, World!\")\n" + " ```\n" +). +-spec write(binary(), binary()) -> {ok, nil} | {error, file_error()}. +write(Filepath, Contents) -> + _pipe = Contents, + _pipe@1 = gleam_stdlib:identity(_pipe), + simplifile_erl:write_bits(Filepath, _pipe@1). + +-file("src/simplifile.gleam", 406). +?DOC( + " Append a bitstring to the contents of a file at the given path\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(Nil) = append_bits(to: \"./needs_more_text.txt\", bits: <<\"more text\":utf8>>)\n" + " ```\n" +). +-spec append_bits(binary(), bitstring()) -> {ok, nil} | {error, file_error()}. +append_bits(Filepath, Bits) -> + simplifile_erl:append_bits(Filepath, Bits). + +-file("src/simplifile.gleam", 367). +?DOC( + " Append a string to the contents of a file at the given path\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(Nil) = append(to: \"./needs_more_text.txt\", contents: \"more text\")\n" + " ```\n" +). +-spec append(binary(), binary()) -> {ok, nil} | {error, file_error()}. +append(Filepath, Contents) -> + _pipe = Contents, + _pipe@1 = gleam_stdlib:identity(_pipe), + simplifile_erl:append_bits(Filepath, _pipe@1). + +-file("src/simplifile.gleam", 420). +?DOC( + " Checks if the provided filepath exists and is a directory.\n" + " Returns an error if it lacks permissions to read the directory.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(True) = is_directory(\"./test\")\n" + " ```\n" +). +-spec is_directory(binary()) -> {ok, boolean()} | {error, file_error()}. +is_directory(Filepath) -> + simplifile_erl:is_directory(Filepath). + +-file("src/simplifile.gleam", 431). +?DOC( + " Create a directory at the provided filepath. Returns an error if\n" + " the directory already exists.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " create_directory(\"./test\")\n" + " ```\n" +). +-spec create_directory(binary()) -> {ok, nil} | {error, file_error()}. +create_directory(Filepath) -> + simplifile_erl:create_directory(Filepath). + +-file("src/simplifile.gleam", 446). +?DOC( + " Create a symbolic link called symlink pointing to target.\n" + " \n" + " ### Footgun Alert \n" + " the target path is relative to *the symlink*,\n" + " not the current working directory. I will likely be updating \n" + " the label on the next major version to reflect that.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " create_symlink(\"../target\", \"./symlink\")\n" + " ```\n" +). +-spec create_symlink(binary(), binary()) -> {ok, nil} | {error, file_error()}. +create_symlink(Target, Symlink) -> + simplifile_erl:create_symlink(Target, Symlink). + +-file("src/simplifile.gleam", 461). +?DOC( + " Create a \"hard link\" called symlink pointing to target.\n" + " This does not have the same relative pathing footgun as \n" + " `create_symlink`.\n" + " \n" + " ## Example\n" + " ```gleam\n" + " create_link(\"../target\", \"./link\")\n" + " ```\n" +). +-spec create_link(binary(), binary()) -> {ok, nil} | {error, file_error()}. +create_link(Target, Link) -> + simplifile_erl:create_link(Target, Link). + +-file("src/simplifile.gleam", 476). +?DOC( + " Lists the contents of a directory.\n" + " The list contains directory and file names, and is not recursive.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(files_and_folders) = read_directory(at: \"./Folder1\")\n" + " ```\n" +). +-spec read_directory(binary()) -> {ok, list(binary())} | {error, file_error()}. +read_directory(Path) -> + simplifile_erl:read_directory(Path). + +-file("src/simplifile.gleam", 488). +?DOC( + " Checks if the file at the provided filepath exists and is a file.\n" + " Returns an Error if it lacks permissions to read the file.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(True) = is_file(\"./test.txt\")\n" + " ```\n" +). +-spec is_file(binary()) -> {ok, boolean()} | {error, file_error()}. +is_file(Filepath) -> + simplifile_erl:is_file(Filepath). + +-file("src/simplifile.gleam", 500). +?DOC( + " Checks if the file at the provided filepath exists and is a symbolic link.\n" + " Returns an Error if it lacks permissions to read the file.\n" + "\n" + " ## Example\n" + " ```gleam\n" + " let assert Ok(True) = is_symlink(\"./symlink\")\n" + " ```\n" +). +-spec is_symlink(binary()) -> {ok, boolean()} | {error, file_error()}. +is_symlink(Filepath) -> + simplifile_erl:is_symlink(Filepath). + +-file("src/simplifile.gleam", 505). +?DOC( + " Creates an empty file at the given filepath. Returns an `Error(Eexist)`\n" + " if the file already exists.\n" +). +-spec create_file(binary()) -> {ok, nil} | {error, file_error()}. +create_file(Filepath) -> + case {begin + _pipe = Filepath, + simplifile_erl:is_file(_pipe) + end, + begin + _pipe@1 = Filepath, + simplifile_erl:is_directory(_pipe@1) + end} of + {{ok, true}, _} -> + {error, eexist}; + + {_, {ok, true}} -> + {error, eexist}; + + {_, _} -> + simplifile_erl:write_bits(Filepath, <<>>) + end. + +-file("src/simplifile.gleam", 516). +?DOC( + " Recursively creates necessary directories for a given directory\n" + " path. Note that if you pass a path that \"looks like\" a file, i.e.\n" + " `./a/b.txt`, a folder named `b.txt` will be created, so be sure\n" + " to pass only the path to the required directory.\n" +). +-spec create_directory_all(binary()) -> {ok, nil} | {error, file_error()}. +create_directory_all(Dirpath) -> + simplifile_erl:create_dir_all(<>). + +-file("src/simplifile.gleam", 546). +?DOC( + " Copy a file at a given path to another path.\n" + " Note: destination should include the filename, not just the directory\n" +). +-spec copy_file(binary(), binary()) -> {ok, nil} | {error, file_error()}. +copy_file(Src, Dest) -> + _pipe = file:copy(Src, Dest), + gleam@result:replace(_pipe, nil). + +-file("src/simplifile.gleam", 560). +?DOC( + " Rename a file at a given path to another path.\n" + " Note: destination should include the filename, not just the directory\n" +). +-spec rename_file(binary(), binary()) -> {ok, nil} | {error, file_error()}. +rename_file(Src, Dest) -> + simplifile_erl:rename_file(Src, Dest). + +-file("src/simplifile.gleam", 565). +?DOC(" Rename a file or directory.\n"). +-spec rename(binary(), binary()) -> {ok, nil} | {error, file_error()}. +rename(Src, Dest) -> + simplifile_erl:rename_file(Src, Dest). + +-file("src/simplifile.gleam", 576). +-spec do_copy_directory(binary(), binary()) -> {ok, nil} | {error, file_error()}. +do_copy_directory(Src, Dest) -> + gleam@result:'try'( + simplifile_erl:read_directory(Src), + fun(Segments) -> + _pipe = Segments, + gleam@list:each( + _pipe, + fun(Segment) -> + Src_path = filepath:join(Src, Segment), + Dest_path = filepath:join(Dest, Segment), + gleam@result:'try'( + simplifile_erl:file_info(Src_path), + fun(Src_info) -> case file_info_type(Src_info) of + file -> + gleam@result:'try'( + simplifile_erl:read_bits(Src_path), + fun(Content) -> _pipe@1 = Content, + simplifile_erl:write_bits( + Dest_path, + _pipe@1 + ) end + ); + + directory -> + gleam@result:'try'( + simplifile_erl:create_directory( + Dest_path + ), + fun(_) -> + do_copy_directory( + Src_path, + Dest_path + ) + end + ); + + symlink -> + {error, + {unknown, + <<"This is an internal bug where the `file_info` is somehow returning info about a simlink. Please file an issue on the simplifile repo."/utf8>>}}; + + other -> + {error, + {unknown, + <<"Unknown file type (not file, directory, or simlink)"/utf8>>}} + end end + ) + end + ), + {ok, nil} + end + ). + +-file("src/simplifile.gleam", 568). +?DOC(" Copy a directory recursively\n"). +-spec copy_directory(binary(), binary()) -> {ok, nil} | {error, file_error()}. +copy_directory(Src, Dest) -> + gleam@result:'try'( + create_directory_all(Dest), + fun(_) -> do_copy_directory(Src, Dest) end + ). + +-file("src/simplifile.gleam", 530). +?DOC( + " Copy a file or a directory to a new path. Copies directories recursively.\n" + " \n" + " ### Performance Note \n" + " This function does work to determine if the src path\n" + " points to a file or a directory. Consider using one of the the dedicated \n" + " functions `copy_file` or `copy_directory` if you already know which one you need.\n" +). +-spec copy(binary(), binary()) -> {ok, nil} | {error, file_error()}. +copy(Src, Dest) -> + gleam@result:'try'( + simplifile_erl:file_info(Src), + fun(Src_info) -> case file_info_type(Src_info) of + file -> + copy_file(Src, Dest); + + directory -> + copy_directory(Src, Dest); + + symlink -> + {error, + {unknown, + <<"This is an internal bug where the `file_info` is somehow returning info about a simlink. Please file an issue on the simplifile repo."/utf8>>}}; + + other -> + {error, + {unknown, + <<"Unknown file type (not file, directory, or simlink)"/utf8>>}} + end end + ). + +-file("src/simplifile.gleam", 612). +?DOC(" Copy a directory recursively and then delete the old one.\n"). +-spec rename_directory(binary(), binary()) -> {ok, nil} | {error, file_error()}. +rename_directory(Src, Dest) -> + gleam@result:'try'( + copy_directory(Src, Dest), + fun(_) -> simplifile_erl:delete(Src) end + ). + +-file("src/simplifile.gleam", 622). +?DOC( + " Clear the contents of a directory, deleting all files and directories within\n" + " but leaving the top level directory in place.\n" +). +-spec clear_directory(binary()) -> {ok, nil} | {error, file_error()}. +clear_directory(Path) -> + gleam@result:'try'( + simplifile_erl:read_directory(Path), + fun(Paths) -> _pipe = Paths, + _pipe@1 = gleam@list:map( + _pipe, + fun(_capture) -> filepath:join(Path, _capture) end + ), + delete_all(_pipe@1) end + ). + +-file("src/simplifile.gleam", 632). +?DOC( + " Returns a list of filepaths for every file in the directory, including nested\n" + " files.\n" +). +-spec get_files(binary()) -> {ok, list(binary())} | {error, file_error()}. +get_files(Directory) -> + gleam@result:'try'( + simplifile_erl:read_directory(Directory), + fun(Contents) -> + gleam@list:try_fold( + Contents, + [], + fun(Acc, Content) -> + Path = filepath:join(Directory, Content), + gleam@result:'try'( + simplifile_erl:file_info(Path), + fun(Info) -> case file_info_type(Info) of + file -> + {ok, [Path | Acc]}; + + directory -> + gleam@result:'try'( + get_files(Path), + fun(Nested_files) -> + {ok, + lists:append(Acc, Nested_files)} + end + ); + + _ -> + {ok, Acc} + end end + ) + end + ) + end + ). + +-file("src/simplifile.gleam", 655). +-spec permission_to_integer(permission()) -> integer(). +permission_to_integer(Permission) -> + case Permission of + read -> + 8#4; + + write -> + 8#2; + + execute -> + 8#1 + end. + +-file("src/simplifile.gleam", 663). +-spec integer_to_permissions(integer()) -> gleam@set:set(permission()). +integer_to_permissions(Integer) -> + case erlang:'band'(Integer, 7) of + 7 -> + gleam@set:from_list([read, write, execute]); + + 6 -> + gleam@set:from_list([read, write]); + + 5 -> + gleam@set:from_list([read, execute]); + + 3 -> + gleam@set:from_list([write, execute]); + + 4 -> + gleam@set:from_list([read]); + + 2 -> + gleam@set:from_list([write]); + + 1 -> + gleam@set:from_list([execute]); + + 0 -> + gleam@set:new(); + + _ -> + erlang:error(#{gleam_error => panic, + message => <<"`panic` expression evaluated."/utf8>>, + file => <>, + module => <<"simplifile"/utf8>>, + function => <<"integer_to_permissions"/utf8>>, + line => 674}) + end. + +-file("src/simplifile.gleam", 687). +-spec file_permissions_to_octal(file_permissions()) -> integer(). +file_permissions_to_octal(Permissions) -> + Make_permission_digit = fun(Permissions@1) -> _pipe = Permissions@1, + _pipe@1 = gleam@set:to_list(_pipe), + _pipe@2 = gleam@list:map(_pipe@1, fun permission_to_integer/1), + gleam@int:sum(_pipe@2) end, + ((Make_permission_digit(erlang:element(2, Permissions)) * 64) + (Make_permission_digit( + erlang:element(3, Permissions) + ) + * 8)) + + Make_permission_digit(erlang:element(4, Permissions)). + +-file("src/simplifile.gleam", 702). +-spec octal_to_file_permissions(integer()) -> file_permissions(). +octal_to_file_permissions(Octal) -> + {file_permissions, + begin + _pipe = Octal, + _pipe@1 = erlang:'bsr'(_pipe, 6), + integer_to_permissions(_pipe@1) + end, + begin + _pipe@2 = Octal, + _pipe@3 = erlang:'bsr'(_pipe@2, 3), + integer_to_permissions(_pipe@3) + end, + begin + _pipe@4 = Octal, + integer_to_permissions(_pipe@4) + end}. + +-file("src/simplifile.gleam", 237). +?DOC(" Extract the `FilePermissions` from a given FileInfo value.\n"). +-spec file_info_permissions(file_info()) -> file_permissions(). +file_info_permissions(File_info) -> + octal_to_file_permissions(file_info_permissions_octal(File_info)). + +-file("src/simplifile.gleam", 738). +?DOC( + " Sets the permissions for a given file using an octal representation\n" + "\n" + " # Example\n" + " ```gleam\n" + " set_permissions_octal(\"./script.sh\", 0o777)\n" + " ```\n" +). +-spec set_permissions_octal(binary(), integer()) -> {ok, nil} | + {error, file_error()}. +set_permissions_octal(Filepath, Permissions) -> + simplifile_erl:set_permissions_octal(Filepath, Permissions). + +-file("src/simplifile.gleam", 723). +?DOC( + " Sets the permissions for a given file\n" + "\n" + " # Example\n" + " ```gleam\n" + " let all = set.from_list([Read, Write, Execute])\n" + " let all = FilePermissions(user: all, group: all, other: all)\n" + " let assert Ok(Nil) = set_permissions(\"./script.sh\", all)\n" + " ```\n" +). +-spec set_permissions(binary(), file_permissions()) -> {ok, nil} | + {error, file_error()}. +set_permissions(Filepath, Permissions) -> + simplifile_erl:set_permissions_octal( + Filepath, + file_permissions_to_octal(Permissions) + ). + +-file("src/simplifile.gleam", 746). +?DOC(" Returns the current working directory\n"). +-spec current_directory() -> {ok, binary()} | {error, file_error()}. +current_directory() -> + _pipe = file:get_cwd(), + gleam@result:map(_pipe, fun gleam_stdlib:utf_codepoint_list_to_string/1). diff --git a/ui/javascript/simplifile/simplifile.mjs b/ui/javascript/simplifile/simplifile.mjs new file mode 100644 index 0000000..401f48f --- /dev/null +++ b/ui/javascript/simplifile/simplifile.mjs @@ -0,0 +1,1053 @@ +import * as $filepath from "../filepath/filepath.mjs"; +import * as $bit_array from "../gleam_stdlib/gleam/bit_array.mjs"; +import * as $int from "../gleam_stdlib/gleam/int.mjs"; +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $result from "../gleam_stdlib/gleam/result.mjs"; +import * as $set from "../gleam_stdlib/gleam/set.mjs"; +import * as $string from "../gleam_stdlib/gleam/string.mjs"; +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + makeError, + toBitArray, +} from "./gleam.mjs"; +import { + fileInfo as file_info, + linkInfo as link_info, + readBits as read_bits, + writeBits as write_bits, + delete_ as delete$, + deleteFile as delete_file, + appendBits as append_bits, + isDirectory as is_directory, + createDirectory as create_directory, + createSymlink as create_symlink, + createLink as create_link, + readDirectory as read_directory, + isFile as is_file, + isSymlink as is_symlink, + createDirAll as do_create_dir_all, + copyFile as do_copy_file, + renameFile as rename_file, + renameFile as rename, + setPermissionsOctal as set_permissions_octal, + currentDirectory as current_directory, +} from "./simplifile_js.mjs"; + +export { + append_bits, + create_directory, + create_link, + create_symlink, + current_directory, + delete$, + delete_file, + file_info, + is_directory, + is_file, + is_symlink, + link_info, + read_bits, + read_directory, + rename, + rename_file, + set_permissions_octal, + write_bits, +}; + +const FILEPATH = "src/simplifile.gleam"; + +/** + * Permission denied. + */ +export class Eacces extends $CustomType {} +export const FileError$Eacces = () => new Eacces(); +export const FileError$isEacces = (value) => value instanceof Eacces; + +/** + * Resource temporarily unavailable. + */ +export class Eagain extends $CustomType {} +export const FileError$Eagain = () => new Eagain(); +export const FileError$isEagain = (value) => value instanceof Eagain; + +/** + * Bad file number + */ +export class Ebadf extends $CustomType {} +export const FileError$Ebadf = () => new Ebadf(); +export const FileError$isEbadf = (value) => value instanceof Ebadf; + +/** + * Bad message. + */ +export class Ebadmsg extends $CustomType {} +export const FileError$Ebadmsg = () => new Ebadmsg(); +export const FileError$isEbadmsg = (value) => value instanceof Ebadmsg; + +/** + * File busy. + */ +export class Ebusy extends $CustomType {} +export const FileError$Ebusy = () => new Ebusy(); +export const FileError$isEbusy = (value) => value instanceof Ebusy; + +/** + * Resource deadlock avoided. + */ +export class Edeadlk extends $CustomType {} +export const FileError$Edeadlk = () => new Edeadlk(); +export const FileError$isEdeadlk = (value) => value instanceof Edeadlk; + +/** + * On most architectures, same as `Edeadlk`. On some architectures, it + * means "File locking deadlock error." + */ +export class Edeadlock extends $CustomType {} +export const FileError$Edeadlock = () => new Edeadlock(); +export const FileError$isEdeadlock = (value) => value instanceof Edeadlock; + +/** + * Disk quota exceeded. + */ +export class Edquot extends $CustomType {} +export const FileError$Edquot = () => new Edquot(); +export const FileError$isEdquot = (value) => value instanceof Edquot; + +/** + * File already exists. + */ +export class Eexist extends $CustomType {} +export const FileError$Eexist = () => new Eexist(); +export const FileError$isEexist = (value) => value instanceof Eexist; + +/** + * Bad address in system call argument. + */ +export class Efault extends $CustomType {} +export const FileError$Efault = () => new Efault(); +export const FileError$isEfault = (value) => value instanceof Efault; + +/** + * File too large. + */ +export class Efbig extends $CustomType {} +export const FileError$Efbig = () => new Efbig(); +export const FileError$isEfbig = (value) => value instanceof Efbig; + +/** + * Inappropriate file type or format. Usually caused by trying to set the + * "sticky bit" on a regular file (not a directory). + */ +export class Eftype extends $CustomType {} +export const FileError$Eftype = () => new Eftype(); +export const FileError$isEftype = (value) => value instanceof Eftype; + +/** + * Interrupted system call. + */ +export class Eintr extends $CustomType {} +export const FileError$Eintr = () => new Eintr(); +export const FileError$isEintr = (value) => value instanceof Eintr; + +/** + * Invalid argument. + */ +export class Einval extends $CustomType {} +export const FileError$Einval = () => new Einval(); +export const FileError$isEinval = (value) => value instanceof Einval; + +/** + * I/O error. + */ +export class Eio extends $CustomType {} +export const FileError$Eio = () => new Eio(); +export const FileError$isEio = (value) => value instanceof Eio; + +/** + * Illegal operation on a directory. + */ +export class Eisdir extends $CustomType {} +export const FileError$Eisdir = () => new Eisdir(); +export const FileError$isEisdir = (value) => value instanceof Eisdir; + +/** + * Too many levels of symbolic links. + */ +export class Eloop extends $CustomType {} +export const FileError$Eloop = () => new Eloop(); +export const FileError$isEloop = (value) => value instanceof Eloop; + +/** + * Too many open files. + */ +export class Emfile extends $CustomType {} +export const FileError$Emfile = () => new Emfile(); +export const FileError$isEmfile = (value) => value instanceof Emfile; + +/** + * Too many links. + */ +export class Emlink extends $CustomType {} +export const FileError$Emlink = () => new Emlink(); +export const FileError$isEmlink = (value) => value instanceof Emlink; + +/** + * Multihop attempted. + */ +export class Emultihop extends $CustomType {} +export const FileError$Emultihop = () => new Emultihop(); +export const FileError$isEmultihop = (value) => value instanceof Emultihop; + +/** + * Filename too long + */ +export class Enametoolong extends $CustomType {} +export const FileError$Enametoolong = () => new Enametoolong(); +export const FileError$isEnametoolong = (value) => + value instanceof Enametoolong; + +/** + * File table overflow + */ +export class Enfile extends $CustomType {} +export const FileError$Enfile = () => new Enfile(); +export const FileError$isEnfile = (value) => value instanceof Enfile; + +/** + * No buffer space available. + */ +export class Enobufs extends $CustomType {} +export const FileError$Enobufs = () => new Enobufs(); +export const FileError$isEnobufs = (value) => value instanceof Enobufs; + +/** + * No such device. + */ +export class Enodev extends $CustomType {} +export const FileError$Enodev = () => new Enodev(); +export const FileError$isEnodev = (value) => value instanceof Enodev; + +/** + * No locks available. + */ +export class Enolck extends $CustomType {} +export const FileError$Enolck = () => new Enolck(); +export const FileError$isEnolck = (value) => value instanceof Enolck; + +/** + * Link has been severed. + */ +export class Enolink extends $CustomType {} +export const FileError$Enolink = () => new Enolink(); +export const FileError$isEnolink = (value) => value instanceof Enolink; + +/** + * No such file or directory. + */ +export class Enoent extends $CustomType {} +export const FileError$Enoent = () => new Enoent(); +export const FileError$isEnoent = (value) => value instanceof Enoent; + +/** + * Not enough memory. + */ +export class Enomem extends $CustomType {} +export const FileError$Enomem = () => new Enomem(); +export const FileError$isEnomem = (value) => value instanceof Enomem; + +/** + * No space left on device. + */ +export class Enospc extends $CustomType {} +export const FileError$Enospc = () => new Enospc(); +export const FileError$isEnospc = (value) => value instanceof Enospc; + +/** + * No STREAM resources. + */ +export class Enosr extends $CustomType {} +export const FileError$Enosr = () => new Enosr(); +export const FileError$isEnosr = (value) => value instanceof Enosr; + +/** + * Not a STREAM. + */ +export class Enostr extends $CustomType {} +export const FileError$Enostr = () => new Enostr(); +export const FileError$isEnostr = (value) => value instanceof Enostr; + +/** + * Function not implemented. + */ +export class Enosys extends $CustomType {} +export const FileError$Enosys = () => new Enosys(); +export const FileError$isEnosys = (value) => value instanceof Enosys; + +/** + * Block device required. + */ +export class Enotblk extends $CustomType {} +export const FileError$Enotblk = () => new Enotblk(); +export const FileError$isEnotblk = (value) => value instanceof Enotblk; + +/** + * Not a directory. + */ +export class Enotdir extends $CustomType {} +export const FileError$Enotdir = () => new Enotdir(); +export const FileError$isEnotdir = (value) => value instanceof Enotdir; + +/** + * Operation not supported. + */ +export class Enotsup extends $CustomType {} +export const FileError$Enotsup = () => new Enotsup(); +export const FileError$isEnotsup = (value) => value instanceof Enotsup; + +/** + * No such device or address. + */ +export class Enxio extends $CustomType {} +export const FileError$Enxio = () => new Enxio(); +export const FileError$isEnxio = (value) => value instanceof Enxio; + +/** + * Operation not supported on socket. + */ +export class Eopnotsupp extends $CustomType {} +export const FileError$Eopnotsupp = () => new Eopnotsupp(); +export const FileError$isEopnotsupp = (value) => value instanceof Eopnotsupp; + +/** + * Value too large to be stored in data type. + */ +export class Eoverflow extends $CustomType {} +export const FileError$Eoverflow = () => new Eoverflow(); +export const FileError$isEoverflow = (value) => value instanceof Eoverflow; + +/** + * Not owner. + */ +export class Eperm extends $CustomType {} +export const FileError$Eperm = () => new Eperm(); +export const FileError$isEperm = (value) => value instanceof Eperm; + +/** + * Broken pipe. + */ +export class Epipe extends $CustomType {} +export const FileError$Epipe = () => new Epipe(); +export const FileError$isEpipe = (value) => value instanceof Epipe; + +/** + * Result too large. + */ +export class Erange extends $CustomType {} +export const FileError$Erange = () => new Erange(); +export const FileError$isErange = (value) => value instanceof Erange; + +/** + * Read-only file system. + */ +export class Erofs extends $CustomType {} +export const FileError$Erofs = () => new Erofs(); +export const FileError$isErofs = (value) => value instanceof Erofs; + +/** + * Invalid seek. + */ +export class Espipe extends $CustomType {} +export const FileError$Espipe = () => new Espipe(); +export const FileError$isEspipe = (value) => value instanceof Espipe; + +/** + * No such process. + */ +export class Esrch extends $CustomType {} +export const FileError$Esrch = () => new Esrch(); +export const FileError$isEsrch = (value) => value instanceof Esrch; + +/** + * Stale remote file handle. + */ +export class Estale extends $CustomType {} +export const FileError$Estale = () => new Estale(); +export const FileError$isEstale = (value) => value instanceof Estale; + +/** + * Text file busy. + */ +export class Etxtbsy extends $CustomType {} +export const FileError$Etxtbsy = () => new Etxtbsy(); +export const FileError$isEtxtbsy = (value) => value instanceof Etxtbsy; + +/** + * Cross-domain link. + */ +export class Exdev extends $CustomType {} +export const FileError$Exdev = () => new Exdev(); +export const FileError$isExdev = (value) => value instanceof Exdev; + +/** + * File was requested to be read as UTF-8, but is not UTF-8 encoded. + */ +export class NotUtf8 extends $CustomType {} +export const FileError$NotUtf8 = () => new NotUtf8(); +export const FileError$isNotUtf8 = (value) => value instanceof NotUtf8; + +/** + * Any error not accounted for by this type + */ +export class Unknown extends $CustomType { + constructor(inner) { + super(); + this.inner = inner; + } +} +export const FileError$Unknown = (inner) => new Unknown(inner); +export const FileError$isUnknown = (value) => value instanceof Unknown; +export const FileError$Unknown$inner = (value) => value.inner; +export const FileError$Unknown$0 = (value) => value.inner; + +export class FileInfo extends $CustomType { + constructor(size, mode, nlinks, inode, user_id, group_id, dev, atime_seconds, mtime_seconds, ctime_seconds) { + super(); + this.size = size; + this.mode = mode; + this.nlinks = nlinks; + this.inode = inode; + this.user_id = user_id; + this.group_id = group_id; + this.dev = dev; + this.atime_seconds = atime_seconds; + this.mtime_seconds = mtime_seconds; + this.ctime_seconds = ctime_seconds; + } +} +export const FileInfo$FileInfo = (size, mode, nlinks, inode, user_id, group_id, dev, atime_seconds, mtime_seconds, ctime_seconds) => + new FileInfo(size, + mode, + nlinks, + inode, + user_id, + group_id, + dev, + atime_seconds, + mtime_seconds, + ctime_seconds); +export const FileInfo$isFileInfo = (value) => value instanceof FileInfo; +export const FileInfo$FileInfo$size = (value) => value.size; +export const FileInfo$FileInfo$0 = (value) => value.size; +export const FileInfo$FileInfo$mode = (value) => value.mode; +export const FileInfo$FileInfo$1 = (value) => value.mode; +export const FileInfo$FileInfo$nlinks = (value) => value.nlinks; +export const FileInfo$FileInfo$2 = (value) => value.nlinks; +export const FileInfo$FileInfo$inode = (value) => value.inode; +export const FileInfo$FileInfo$3 = (value) => value.inode; +export const FileInfo$FileInfo$user_id = (value) => value.user_id; +export const FileInfo$FileInfo$4 = (value) => value.user_id; +export const FileInfo$FileInfo$group_id = (value) => value.group_id; +export const FileInfo$FileInfo$5 = (value) => value.group_id; +export const FileInfo$FileInfo$dev = (value) => value.dev; +export const FileInfo$FileInfo$6 = (value) => value.dev; +export const FileInfo$FileInfo$atime_seconds = (value) => value.atime_seconds; +export const FileInfo$FileInfo$7 = (value) => value.atime_seconds; +export const FileInfo$FileInfo$mtime_seconds = (value) => value.mtime_seconds; +export const FileInfo$FileInfo$8 = (value) => value.mtime_seconds; +export const FileInfo$FileInfo$ctime_seconds = (value) => value.ctime_seconds; +export const FileInfo$FileInfo$9 = (value) => value.ctime_seconds; + +/** + * A regular file + */ +export class File extends $CustomType {} +export const FileType$File = () => new File(); +export const FileType$isFile = (value) => value instanceof File; + +/** + * A directory + */ +export class Directory extends $CustomType {} +export const FileType$Directory = () => new Directory(); +export const FileType$isDirectory = (value) => value instanceof Directory; + +/** + * A symbolic link + */ +export class Symlink extends $CustomType {} +export const FileType$Symlink = () => new Symlink(); +export const FileType$isSymlink = (value) => value instanceof Symlink; + +/** + * Another special file type present on some systems, lika a socket or device + */ +export class Other extends $CustomType {} +export const FileType$Other = () => new Other(); +export const FileType$isOther = (value) => value instanceof Other; + +export class Read extends $CustomType {} +export const Permission$Read = () => new Read(); +export const Permission$isRead = (value) => value instanceof Read; + +export class Write extends $CustomType {} +export const Permission$Write = () => new Write(); +export const Permission$isWrite = (value) => value instanceof Write; + +export class Execute extends $CustomType {} +export const Permission$Execute = () => new Execute(); +export const Permission$isExecute = (value) => value instanceof Execute; + +export class FilePermissions extends $CustomType { + constructor(user, group, other) { + super(); + this.user = user; + this.group = group; + this.other = other; + } +} +export const FilePermissions$FilePermissions = (user, group, other) => + new FilePermissions(user, group, other); +export const FilePermissions$isFilePermissions = (value) => + value instanceof FilePermissions; +export const FilePermissions$FilePermissions$user = (value) => value.user; +export const FilePermissions$FilePermissions$0 = (value) => value.user; +export const FilePermissions$FilePermissions$group = (value) => value.group; +export const FilePermissions$FilePermissions$1 = (value) => value.group; +export const FilePermissions$FilePermissions$other = (value) => value.other; +export const FilePermissions$FilePermissions$2 = (value) => value.other; + +/** + * Convert an error into a human-readable description + * ## Example + * ```gleam + * let assert "Input/output error" = describe_error(Eio) + * ``` + */ +export function describe_error(error) { + if (error instanceof Eacces) { + return "Permission denied"; + } else if (error instanceof Eagain) { + return "Resource temporarily unavailable"; + } else if (error instanceof Ebadf) { + return "Bad file descriptor"; + } else if (error instanceof Ebadmsg) { + return "Bad message"; + } else if (error instanceof Ebusy) { + return "Resource busy"; + } else if (error instanceof Edeadlk) { + return "Resource deadlock avoided"; + } else if (error instanceof Edeadlock) { + return "Resource deadlock avoided"; + } else if (error instanceof Edquot) { + return "Disc quota exceeded"; + } else if (error instanceof Eexist) { + return "File exists"; + } else if (error instanceof Efault) { + return "Bad address"; + } else if (error instanceof Efbig) { + return "File too large"; + } else if (error instanceof Eftype) { + return "Inappropriate file type or format"; + } else if (error instanceof Eintr) { + return "Interrupted system call"; + } else if (error instanceof Einval) { + return "Invalid argument"; + } else if (error instanceof Eio) { + return "Input/output error"; + } else if (error instanceof Eisdir) { + return "Is a directory"; + } else if (error instanceof Eloop) { + return "Too many levels of symbolic links"; + } else if (error instanceof Emfile) { + return "Too many open files"; + } else if (error instanceof Emlink) { + return "Too many links"; + } else if (error instanceof Emultihop) { + return "Multihop attempted"; + } else if (error instanceof Enametoolong) { + return "File name too long"; + } else if (error instanceof Enfile) { + return "Too many open files in system"; + } else if (error instanceof Enobufs) { + return "No buffer space available"; + } else if (error instanceof Enodev) { + return "Operation not supported by device"; + } else if (error instanceof Enolck) { + return "No locks available"; + } else if (error instanceof Enolink) { + return "Link has been severed"; + } else if (error instanceof Enoent) { + return "No such file or directory"; + } else if (error instanceof Enomem) { + return "Cannot allocate memory"; + } else if (error instanceof Enospc) { + return "No space left on device"; + } else if (error instanceof Enosr) { + return "No STREAM resources"; + } else if (error instanceof Enostr) { + return "Not a STREAM"; + } else if (error instanceof Enosys) { + return "Function not implemented"; + } else if (error instanceof Enotblk) { + return "Block device required"; + } else if (error instanceof Enotdir) { + return "Not a directory"; + } else if (error instanceof Enotsup) { + return "Operation not supported"; + } else if (error instanceof Enxio) { + return "Device not configured"; + } else if (error instanceof Eopnotsupp) { + return "Operation not supported on socket"; + } else if (error instanceof Eoverflow) { + return "Value too large to be stored in data type"; + } else if (error instanceof Eperm) { + return "Operation not permitted"; + } else if (error instanceof Epipe) { + return "Broken pipe"; + } else if (error instanceof Erange) { + return "Result too large"; + } else if (error instanceof Erofs) { + return "Read-only file system"; + } else if (error instanceof Espipe) { + return "Illegal seek"; + } else if (error instanceof Esrch) { + return "No such process"; + } else if (error instanceof Estale) { + return "Stale NFS file handle"; + } else if (error instanceof Etxtbsy) { + return "Text file busy"; + } else if (error instanceof Exdev) { + return "Cross-device link"; + } else if (error instanceof NotUtf8) { + return "File not UTF-8 encoded"; + } else { + let inner = error.inner; + return "Unknown error: " + inner; + } +} + +/** + * Extract the file permissions from a given FileInfo value in their octal representation. + * + * ## Example + * ```gleam + * use info <- result.try(simplifile.file_info("src/app.gleam")) + * simplifile.file_info_permissions_octal(info) + * // --> 0o644 + * ``` + */ +export function file_info_permissions_octal(file_info) { + return $int.bitwise_and(file_info.mode, 0o777); +} + +function integer_to_permissions(integer) { + let $ = $int.bitwise_and(integer, 7); + if ($ === 7) { + return $set.from_list(toList([new Read(), new Write(), new Execute()])); + } else if ($ === 6) { + return $set.from_list(toList([new Read(), new Write()])); + } else if ($ === 5) { + return $set.from_list(toList([new Read(), new Execute()])); + } else if ($ === 3) { + return $set.from_list(toList([new Write(), new Execute()])); + } else if ($ === 4) { + return $set.from_list(toList([new Read()])); + } else if ($ === 2) { + return $set.from_list(toList([new Write()])); + } else if ($ === 1) { + return $set.from_list(toList([new Execute()])); + } else if ($ === 0) { + return $set.new$(); + } else { + throw makeError( + "panic", + FILEPATH, + "simplifile", + 674, + "integer_to_permissions", + "`panic` expression evaluated.", + {} + ) + } +} + +function octal_to_file_permissions(octal) { + return new FilePermissions( + (() => { + let _pipe = octal; + let _pipe$1 = $int.bitwise_shift_right(_pipe, 6); + return integer_to_permissions(_pipe$1); + })(), + (() => { + let _pipe = octal; + let _pipe$1 = $int.bitwise_shift_right(_pipe, 3); + return integer_to_permissions(_pipe$1); + })(), + (() => { + let _pipe = octal; + return integer_to_permissions(_pipe); + })(), + ); +} + +/** + * Extract the `FilePermissions` from a given FileInfo value. + */ +export function file_info_permissions(file_info) { + return octal_to_file_permissions(file_info_permissions_octal(file_info)); +} + +/** + * Extract the file type from a given FileInfo value. + * + * ## Example + * ```gleam + * use info <- result.try(simplifile.file_info("src/app.gleam")) + * simplifile.file_info_type(info) + * // --> simplifile.File + * ``` + */ +export function file_info_type(file_info) { + let $ = $int.bitwise_and(file_info.mode, 0o170000); + if ($ === 32768) { + return new File(); + } else if ($ === 16384) { + return new Directory(); + } else if ($ === 40960) { + return new Symlink(); + } else { + return new Other(); + } +} + +/** + * Read a files contents as a string + * ## Example + * ```gleam + * let assert Ok(records) = read(from: "./users.csv") + * ``` + */ +export function read(filepath) { + let $ = read_bits(filepath); + if ($ instanceof Ok) { + let bits = $[0]; + let $1 = $bit_array.to_string(bits); + if ($1 instanceof Ok) { + return $1; + } else { + return new Error(new NotUtf8()); + } + } else { + return $; + } +} + +/** + * Write a string to a file at the given path + * ## Example + * ```gleam + * let assert Ok(Nil) = write(to: "./hello_world.txt", contents: "Hello, World!") + * ``` + */ +export function write(filepath, contents) { + let _pipe = contents; + let _pipe$1 = $bit_array.from_string(_pipe); + return write_bits(filepath, _pipe$1); +} + +/** + * Delete all files/directories specified in a list of paths. + * Recursively deletes provided directories. + * Does not return an error if one or more of the provided paths + * do not exist. + */ +export function delete_all(loop$paths) { + while (true) { + let paths = loop$paths; + if (paths instanceof $Empty) { + return new Ok(undefined); + } else { + let path = paths.head; + let rest = paths.tail; + let $ = delete$(path); + if ($ instanceof Ok) { + loop$paths = rest; + } else { + let $1 = $[0]; + if ($1 instanceof Enoent) { + loop$paths = rest; + } else { + return $; + } + } + } + } +} + +/** + * Append a string to the contents of a file at the given path + * ## Example + * ```gleam + * let assert Ok(Nil) = append(to: "./needs_more_text.txt", contents: "more text") + * ``` + */ +export function append(filepath, contents) { + let _pipe = contents; + let _pipe$1 = $bit_array.from_string(_pipe); + return append_bits(filepath, _pipe$1); +} + +/** + * Creates an empty file at the given filepath. Returns an `Error(Eexist)` + * if the file already exists. + */ +export function create_file(filepath) { + let $ = (() => { + let _pipe = filepath; + return is_file(_pipe); + })(); + let $1 = (() => { + let _pipe = filepath; + return is_directory(_pipe); + })(); + if ($ instanceof Ok) { + let $2 = $[0]; + if ($2) { + return new Error(new Eexist()); + } else if ($1 instanceof Ok) { + let $3 = $1[0]; + if ($3) { + return new Error(new Eexist()); + } else { + return write_bits(filepath, toBitArray([])); + } + } else { + return write_bits(filepath, toBitArray([])); + } + } else if ($1 instanceof Ok) { + let $2 = $1[0]; + if ($2) { + return new Error(new Eexist()); + } else { + return write_bits(filepath, toBitArray([])); + } + } else { + return write_bits(filepath, toBitArray([])); + } +} + +/** + * Recursively creates necessary directories for a given directory + * path. Note that if you pass a path that "looks like" a file, i.e. + * `./a/b.txt`, a folder named `b.txt` will be created, so be sure + * to pass only the path to the required directory. + */ +export function create_directory_all(dirpath) { + return do_create_dir_all(dirpath + "/"); +} + +function do_copy_directory(src, dest) { + return $result.try$( + read_directory(src), + (segments) => { + let _pipe = segments; + $list.each( + _pipe, + (segment) => { + let src_path = $filepath.join(src, segment); + let dest_path = $filepath.join(dest, segment); + return $result.try$( + file_info(src_path), + (src_info) => { + let $ = file_info_type(src_info); + if ($ instanceof File) { + return $result.try$( + read_bits(src_path), + (content) => { + let _pipe$1 = content; + return write_bits(dest_path, _pipe$1); + }, + ); + } else if ($ instanceof Directory) { + return $result.try$( + create_directory(dest_path), + (_) => { return do_copy_directory(src_path, dest_path); }, + ); + } else if ($ instanceof Symlink) { + return new Error( + new Unknown( + "This is an internal bug where the `file_info` is somehow returning info about a simlink. Please file an issue on the simplifile repo.", + ), + ); + } else { + return new Error( + new Unknown( + "Unknown file type (not file, directory, or simlink)", + ), + ); + } + }, + ); + }, + ); + return new Ok(undefined); + }, + ); +} + +/** + * Copy a directory recursively + */ +export function copy_directory(src, dest) { + return $result.try$( + create_directory_all(dest), + (_) => { return do_copy_directory(src, dest); }, + ); +} + +/** + * Copy a file at a given path to another path. + * Note: destination should include the filename, not just the directory + */ +export function copy_file(src, dest) { + let _pipe = do_copy_file(src, dest); + return $result.replace(_pipe, undefined); +} + +/** + * Copy a file or a directory to a new path. Copies directories recursively. + * + * ### Performance Note + * This function does work to determine if the src path + * points to a file or a directory. Consider using one of the the dedicated + * functions `copy_file` or `copy_directory` if you already know which one you need. + */ +export function copy(src, dest) { + return $result.try$( + file_info(src), + (src_info) => { + let $ = file_info_type(src_info); + if ($ instanceof File) { + return copy_file(src, dest); + } else if ($ instanceof Directory) { + return copy_directory(src, dest); + } else if ($ instanceof Symlink) { + return new Error( + new Unknown( + "This is an internal bug where the `file_info` is somehow returning info about a simlink. Please file an issue on the simplifile repo.", + ), + ); + } else { + return new Error( + new Unknown("Unknown file type (not file, directory, or simlink)"), + ); + } + }, + ); +} + +/** + * Copy a directory recursively and then delete the old one. + */ +export function rename_directory(src, dest) { + return $result.try$( + copy_directory(src, dest), + (_) => { return delete$(src); }, + ); +} + +/** + * Clear the contents of a directory, deleting all files and directories within + * but leaving the top level directory in place. + */ +export function clear_directory(path) { + return $result.try$( + read_directory(path), + (paths) => { + let _pipe = paths; + let _pipe$1 = $list.map( + _pipe, + (_capture) => { return $filepath.join(path, _capture); }, + ); + return delete_all(_pipe$1); + }, + ); +} + +/** + * Returns a list of filepaths for every file in the directory, including nested + * files. + */ +export function get_files(directory) { + return $result.try$( + read_directory(directory), + (contents) => { + return $list.try_fold( + contents, + toList([]), + (acc, content) => { + let path = $filepath.join(directory, content); + return $result.try$( + file_info(path), + (info) => { + let $ = file_info_type(info); + if ($ instanceof File) { + return new Ok(listPrepend(path, acc)); + } else if ($ instanceof Directory) { + return $result.try$( + get_files(path), + (nested_files) => { + return new Ok($list.append(acc, nested_files)); + }, + ); + } else { + return new Ok(acc); + } + }, + ); + }, + ); + }, + ); +} + +function permission_to_integer(permission) { + if (permission instanceof Read) { + return 0o4; + } else if (permission instanceof Write) { + return 0o2; + } else { + return 0o1; + } +} + +export function file_permissions_to_octal(permissions) { + let make_permission_digit = (permissions) => { + let _pipe = permissions; + let _pipe$1 = $set.to_list(_pipe); + let _pipe$2 = $list.map(_pipe$1, permission_to_integer); + return $int.sum(_pipe$2); + }; + return (make_permission_digit(permissions.user) * 64 + make_permission_digit( + permissions.group, + ) * 8) + make_permission_digit(permissions.other); +} + +/** + * Sets the permissions for a given file + * + * # Example + * ```gleam + * let all = set.from_list([Read, Write, Execute]) + * let all = FilePermissions(user: all, group: all, other: all) + * let assert Ok(Nil) = set_permissions("./script.sh", all) + * ``` + */ +export function set_permissions(filepath, permissions) { + return set_permissions_octal(filepath, file_permissions_to_octal(permissions)); +} diff --git a/ui/javascript/simplifile/simplifile_erl.erl b/ui/javascript/simplifile/simplifile_erl.erl new file mode 100644 index 0000000..f997758 --- /dev/null +++ b/ui/javascript/simplifile/simplifile_erl.erl @@ -0,0 +1,244 @@ +%%% -------------------------------------------------- +%%% @author Benjamin Peinhardt +%%% @doc Erlang ffi glue code for the simplifile Gleam package. +%%% @end +%%% -------------------------------------------------- + +-module(simplifile_erl). + +-compile({no_auto_import,[link/2]}). + +%% API +-export([ + append_bits/2, + create_directory/1, + create_dir_all/1, + delete_file/1, + create_link/2, + create_symlink/2, + delete/1, + delete_directory/1, + file_info/1, + link_info/1, + is_directory/1, + is_file/1, + is_symlink/1, + read_bits/1, + read_directory/1, + rename_file/2, + set_permissions_octal/2, + write_bits/2 +]). + +-include_lib("kernel/include/file.hrl"). + +%% A macro for checking whether the error returned is one of the atoms for a posixe error. +-define(is_posix_error(Error), + Error =:= eacces + orelse Error =:= eagain + orelse Error =:= ebadf + orelse Error =:= ebadmsg + orelse Error =:= ebusy + orelse Error =:= edeadlk + orelse Error =:= edeadlock + orelse Error =:= edquot + orelse Error =:= eexist + orelse Error =:= efault + orelse Error =:= efbig + orelse Error =:= eftype + orelse Error =:= eintr + orelse Error =:= einval + orelse Error =:= eio + orelse Error =:= eisdir + orelse Error =:= eloop + orelse Error =:= emfile + orelse Error =:= emlink + orelse Error =:= emultihop + orelse Error =:= enametoolong + orelse Error =:= enfile + orelse Error =:= enobufs + orelse Error =:= enodev + orelse Error =:= enolck + orelse Error =:= enolink + orelse Error =:= enoent + orelse Error =:= enomem + orelse Error =:= enospc + orelse Error =:= enosr + orelse Error =:= enostr + orelse Error =:= enosys + orelse Error =:= enotblk + orelse Error =:= enotdir + orelse Error =:= enotsup + orelse Error =:= enxio + orelse Error =:= eopnotsupp + orelse Error =:= eoverflow + orelse Error =:= eperm + orelse Error =:= epipe + orelse Error =:= erange + orelse Error =:= erofs + orelse Error =:= espipe + orelse Error =:= esrch + orelse Error =:= estale + orelse Error =:= etxtbsy + orelse Error =:= exdev). + +%% Only return the error if it's a posix error. Also converts ok to {ok, nil}, +%% as gleam doesn't have atoms. +posix_result(Result) -> + case Result of + ok -> + {ok, nil}; + {ok, Value} -> + {ok, Value}; + {error, Reason} when ?is_posix_error(Reason) -> + {error, Reason}; + {error, Reason} -> + {error, {unknown, string:uppercase(atom_to_binary(Reason))}} + end. + +%% Read the binary contents of a file +read_bits(Filename) -> + posix_result(file:read_file(Filename)). + +%% Write bytes to a file +write_bits(Filename, Contents) -> + case bit_size(Contents) rem 8 of + 0 -> posix_result(file:write_file(Filename, Contents, [raw])); + _ -> {error, einval} + end. + +%% Append bytes to a file +append_bits(Filename, Contents) -> + case bit_size(Contents) rem 8 of + 0 -> posix_result(file:write_file(Filename, Contents, [append, raw])); + _ -> {error, einval} + end. + +%% Delete the file at the given path +delete_file(Filename) -> + posix_result(file:delete(Filename, [raw])). + +%% Create a directory at the given path. Missing parent directories are not created. +create_directory(Dir) -> + posix_result(file:make_dir(Dir)). + +%% Create a symbolic link New to the file or directory Existing (does not need to exist). +create_symlink(Existing, New) -> + posix_result(file:make_symlink(Existing, New)). + +%% Create a "hard link" New to the file or directory Existing. +create_link(Existing, New) -> + posix_result(file:make_link(Existing, New)). + +%% List the contents of a directory +read_directory(Dir) -> + case file:list_dir(Dir) of + {ok, Filenames} -> + {ok, [unicode:characters_to_binary(Filename) || Filename <- Filenames]}; + {error, Reason} when ?is_posix_error(Reason) -> + {error, Reason} + end. + +%% Delete a directory at the given path. Directory must be empty. +delete_directory(Dir) -> + posix_result(file:del_dir(Dir)). + +%% Deletes a file/directory and everything in it. +delete(Dir) -> + posix_result(file:del_dir_r(Dir)). + +%% Creates the entire path for a given directory to exist +create_dir_all(Filename) -> + posix_result(filelib:ensure_path(Filename)). + +%% Move file from one path to another +rename_file(Source, Destination) -> + posix_result(file:rename(Source, Destination)). + +%% Set the permissions for the given file. +set_permissions_octal(Filename, Permissions) -> + posix_result(file:change_mode(Filename, Permissions)). + +is_directory(Path) -> + case file:read_file_info(Path) of + {ok, FileInfo} -> + case FileInfo#file_info.type of + directory -> + {ok, true}; + _ -> + {ok, false} + end; + {error, enoent} -> + {ok, false}; + {error, Reason} -> + posix_result({error, Reason}) + end. + +is_file(Path) -> + case file:read_file_info(Path) of + {ok, FileInfo} -> + case FileInfo#file_info.type of + regular -> + {ok, true}; + _ -> + {ok, false} + end; + {error, enoent} -> + {ok, false}; + {error, Reason} -> + posix_result({error, Reason}) + end. + +is_symlink(Path) -> + case file:read_link_info(Path) of + {ok, FileInfo} -> + case FileInfo#file_info.type of + symlink -> + {ok, true}; + _ -> + {ok, false} + end; + {error, enoent} -> + {ok, false}; + {error, Reason} -> + posix_result({error, Reason}) + end. + +file_info_result(Result) -> + case Result of + {ok, + {file_info, + Size, + _Type, + _Access, + Atime, + Mtime, + Ctime, + Mode, + Links, + MajorDevice, + _MinorDevice, + Inode, + Uid, + Gid}} -> + {ok, + {file_info, + Size, + Mode, + Links, + Inode, + Uid, + Gid, + MajorDevice, + Atime, + Mtime, + Ctime}}; + {error, Reason} when ?is_posix_error(Reason) -> + Result + end. + +file_info(Filename) -> + file_info_result(file:read_file_info(Filename, [{time, posix}])). + +link_info(Filename) -> + file_info_result(file:read_link_info(Filename, [{time, posix}])). diff --git a/ui/javascript/simplifile/simplifile_js.mjs b/ui/javascript/simplifile/simplifile_js.mjs new file mode 100644 index 0000000..6b82923 --- /dev/null +++ b/ui/javascript/simplifile/simplifile_js.mjs @@ -0,0 +1,412 @@ +// @ts-check + +import fs from "node:fs"; +import path from "node:path"; +import process from "node:process"; +import { BitArray, Ok, Error as GError, toList } from "./gleam.mjs"; +import * as $simplifile from "./simplifile.mjs"; + +/** + * Read the contents of a file as a BitArray + * + * @param {string} filepath + * @returns {Ok | GError} A Result with the BitArray of the file contents + */ +export function readBits(filepath) { + return gleamResult(() => { + const contents = fs.readFileSync(path.normalize(filepath)); + return new BitArray(new Uint8Array(contents)); + }); +} + +/** + * Write the given BitArray to a file + * + * @param {string} filepath + * @param {BitArray} contents + * @returns {Ok | GError} + */ +export function writeBits(filepath, contents) { + return gleamResult(() => + fs.writeFileSync(path.normalize(filepath), toUint8Array(contents)) + ); +} + +/** + * Append the given BitArray to a file + * + * @param {string} filepath + * @param {BitArray} contents + * @returns {Ok | GError} + */ +export function appendBits(filepath, contents) { + return gleamResult(() => + fs.appendFileSync(path.normalize(filepath), toUint8Array(contents)) + ); +} + +/** + * Returns a BitArray as an aligned Uint8Array. Avoids a copy when possible. + * + * @param {BitArray} contents + * @returns Uint8Array + */ +function toUint8Array(contents) { + if (contents.bitSize % 8 !== 0) { + throw new GError(new $simplifile.Einval()); + } + + let buffer = contents.rawBuffer; + if (contents.bitOffset !== 0) { + buffer = new Uint8Array(contents.byteSize); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = contents.byteAt(i); + } + } + + return buffer; +} + +/** + * Check whether a file exists at the given path + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function isFile(filepath) { + try { + return new Ok(fs.statSync(path.normalize(filepath)).isFile()); + } catch (e) { + if (e.code === "ENOENT") { + return new Ok(false); + } else { + return new GError(cast_error(e.code)); + } + } +} + +/** + * Check whether a symbolic link exists at the given path + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function isSymlink(filepath) { + try { + return new Ok(fs.lstatSync(path.normalize(filepath)).isSymbolicLink()); + } catch (e) { + if (e.code === "ENOENT") { + return new Ok(false); + } else { + return new GError(cast_error(e.code)); + } + } +} + +/** + * Check whether a directory exists at the given path + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function isDirectory(filepath) { + try { + return new Ok(fs.statSync(path.normalize(filepath)).isDirectory()); + } catch (e) { + if (e.code === "ENOENT") { + return new Ok(false); + } else { + return new GError(cast_error(e.code)); + } + } +} + +/** + * Create the symbolic link called path pointing to target + * + * @param {string} target + * @param {string} path + * @returns {Ok | GError} + */ +export function createSymlink(target, path) { + return gleamResult(() => fs.symlinkSync(target, path)); +} + +/** + * Create the "hard link" called path pointing to the target + * + * @param {string} target + * @param {string} path + * returns {ok | GError} + */ +export function createLink(target, path) { + return gleamResult(() => fs.linkSync(target, path)); +} + +/** + * Create a directory at the given filepath + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function createDirectory(filepath) { + return gleamResult(() => fs.mkdirSync(path.normalize(filepath))); +} + +/** + * Recursively create a directory structure from the given path. + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function createDirAll(filepath) { + return gleamResult(() => { + fs.mkdirSync(path.normalize(filepath), { recursive: true }); + }); +} + +/** + * Recursively delete a directory or delete a file at the given path. + * + * @param {string} fileOrDirPath + * @returns {Ok | GError} + */ +export function delete_(fileOrDirPath) { + return gleamResult(() => { + const isDir = isDirectory(fileOrDirPath); + if (isDir instanceof Ok && isDir[0] === true) { + fs.rmSync(path.normalize(fileOrDirPath), { recursive: true }); + } else { + fs.unlinkSync(path.normalize(fileOrDirPath)); + } + }); +} + +/** + * Specifically delete a single file. + * + * @param {string} filePath + * @returns {Ok | GError} + */ +export function deleteFile(filePath) { + return gleamResult(() => { + fs.unlinkSync(path.normalize(filePath)); + }) +} + +/** + * List the contents of a directory. + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function readDirectory(filepath) { + return gleamResult(() => toList(fs.readdirSync(path.normalize(filepath)))); +} + +/** + * Copy a file to a new path. + * + * @param {string} srcpath + * @param {string} destpath + * @returns {Ok | GError} + */ +export function copyFile(srcpath, destpath) { + return gleamResult(() => + fs.copyFileSync(path.normalize(srcpath), path.normalize(destpath)), + ); +} + +/** + * Move a file to the new path. + * + * @param {string} srcpath + * @param {string} destpath + * @returns {Ok | GError} + */ +export function renameFile(srcpath, destpath) { + return gleamResult(() => + fs.renameSync(path.normalize(srcpath), path.normalize(destpath)), + ); +} + +/** + * Set the file permissions. Octal number should be in base 8. + * + * @param {string} filepath + * @param {number} octalNumber + * @returns {Ok | GError} + */ +export function setPermissionsOctal(filepath, octalNumber) { + return gleamResult(() => fs.chmodSync(path.normalize(filepath), octalNumber)); +} + +/** + * Return the current directory. + * + * @returns {Ok | GError} The current directory + */ +export function currentDirectory() { + return gleamResult(() => process.cwd()); +} + +/** + * + * @param {string} filepath + * @returns {Ok | GError} + */ +export function fileInfo(filepath) { + return gleamResult(() => { + const stat = fs.statSync(path.normalize(filepath)); + return new FileInfo(stat); + }); +} + +/** + * @param {string} filepath + * @returns {Ok | GError} + */ +export function linkInfo(filepath) { + return gleamResult(() => { + const stat = fs.lstatSync(path.normalize(filepath)); + return new FileInfo(stat); + }); +} + +class FileInfo { + /** + * @param {fs.Stats} stat + */ + constructor(stat) { + this.size = stat.size; + this.mode = stat.mode; + this.nlinks = stat.nlink; + this.inode = stat.ino; + this.user_id = stat.uid; + this.group_id = stat.gid; + this.dev = stat.dev; + this.atime_seconds = Math.floor(stat.atimeMs / 1000); + this.mtime_seconds = Math.floor(stat.mtimeMs / 1000); + this.ctime_seconds = Math.floor(stat.ctimeMs / 1000); + } +} + +/** + * Perform some operation and return a Gleam `Result(a, String)` + * where `a` is the type returned by the operation and the `String` + * is the error code. + * + * @param {function():any} op + * @returns {Ok | GError} + */ +function gleamResult(op) { + try { + const val = op(); + return new Ok(val); + } catch (e) { + return new GError(cast_error(e.code)); + } +} + +function cast_error(error_code) { + switch (error_code) { + case "EACCES": + return new $simplifile.Eacces(); + case "EAGAIN": + return new $simplifile.Eagain(); + case "EBADF": + return new $simplifile.Ebadf(); + case "EBADMSG": + return new $simplifile.Ebadmsg(); + case "EBUSY": + return new $simplifile.Ebusy(); + case "EDEADLK": + return new $simplifile.Edeadlk(); + case "EDEADLOCK": + return new $simplifile.Edeadlock(); + case "EDQUOT": + return new $simplifile.Edquot(); + case "EEXIST": + return new $simplifile.Eexist(); + case "EFAULT": + return new $simplifile.Efault(); + case "EFBIG": + return new $simplifile.Efbig(); + case "EFTYPE": + return new $simplifile.Eftype(); + case "EINTR": + return new $simplifile.Eintr(); + case "EINVAL": + return new $simplifile.Einval(); + case "EIO": + return new $simplifile.Eio(); + case "EISDIR": + return new $simplifile.Eisdir(); + case "ELOOP": + return new $simplifile.Eloop(); + case "EMFILE": + return new $simplifile.Emfile(); + case "EMLINK": + return new $simplifile.Emlink(); + case "EMULTIHOP": + return new $simplifile.Emultihop(); + case "ENAMETOOLONG": + return new $simplifile.Enametoolong(); + case "ENFILE": + return new $simplifile.Enfile(); + case "ENOBUFS": + return new $simplifile.Enobufs(); + case "ENODEV": + return new $simplifile.Enodev(); + case "ENOLCK": + return new $simplifile.Enolck(); + case "ENOLINK": + return new $simplifile.Enolink(); + case "ENOENT": + return new $simplifile.Enoent(); + case "ENOMEM": + return new $simplifile.Enomem(); + case "ENOSPC": + return new $simplifile.Enospc(); + case "ENOSR": + return new $simplifile.Enosr(); + case "ENOSTR": + return new $simplifile.Enostr(); + case "ENOSYS": + return new $simplifile.Enosys(); + case "ENOBLK": + return new $simplifile.Enotblk(); + case "ENOTDIR": + return new $simplifile.Enotdir(); + case "ENOTSUP": + return new $simplifile.Enotsup(); + case "ENXIO": + return new $simplifile.Enxio(); + case "EOPNOTSUPP": + return new $simplifile.Eopnotsupp(); + case "EOVERFLOW": + return new $simplifile.Eoverflow(); + case "EPERM": + return new $simplifile.Eperm(); + case "EPIPE": + return new $simplifile.Epipe(); + case "ERANGE": + return new $simplifile.Erange(); + case "EROFS": + return new $simplifile.Erofs(); + case "ESPIPE": + return new $simplifile.Espipe(); + case "ESRCH": + return new $simplifile.Esrch(); + case "ESTALE": + return new $simplifile.Estale(); + case "ETXTBSY": + return new $simplifile.Etxtbsy(); + case "EXDEV": + return new $simplifile.Exdev(); + case "NOTUTF8": + return new $simplifile.NotUtf8(); + default: + return new $simplifile.Unknown(error_code); + } +} diff --git a/ui/main.js b/ui/main.js new file mode 100644 index 0000000..308b596 --- /dev/null +++ b/ui/main.js @@ -0,0 +1,34 @@ +import { testing } from "./javascript/markdown2/markdown2.mjs"; + +let data = JSON.parse( + testing("#1 heading #- !1 important !- @2 true @- >3 quote >- "), +); +let output = ""; +let hirachy = []; +let marking = []; +let quote = []; +let truth = []; +for (var i = 0; i < data.length; i++) { + let item = data[i]; + //{type: 'marking', value: 2} + // console.log(item); + if (item.type == "text") { + let markingvalue = marking.at(-1) ?? 0; + console.log(markingvalue); + output = + output + + `

` + + item.value + + "

"; + } + if (item.type == "marking") { + marking.push(item.value); + } + if (item.type == "pop") { + marking.pop(); + } + // console.log(output); +} +document.addEventListener("DOMContentLoaded", () => { + document.getElementById("text").innerHTML = output; +});