├── .envrc
├── .github
├── FUNDING.yml
└── workflows
│ └── test.yml
├── .gitignore
├── .prettierrc.json
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── LICENSE
├── README.md
├── devbox.json
├── devbox.lock
├── gren.json
├── hints
├── 0.md
├── 1.md
├── 10.md
├── 11.md
├── 2.md
├── 3.md
├── 4.md
├── 5.md
├── 6.md
├── 8.md
└── 9.md
├── integration_tests
├── gren.json
├── run-tests.sh
└── src
│ ├── Main.gren
│ └── Test
│ ├── Crypto.gren
│ └── Task.gren
├── src
├── Array.gren
├── Array
│ └── Builder.gren
├── Basics.gren
├── Bitwise.gren
├── Bytes.gren
├── Bytes
│ ├── Decode.gren
│ └── Encode.gren
├── Char.gren
├── Crypto.gren
├── Debug.gren
├── Dict.gren
├── Gren
│ └── Kernel
│ │ ├── Array.js
│ │ ├── Basics.js
│ │ ├── Bitwise.js
│ │ ├── Bytes.js
│ │ ├── Char.js
│ │ ├── Crypto.js
│ │ ├── Debug.js
│ │ ├── Json.js
│ │ ├── Math.js
│ │ ├── Platform.js
│ │ ├── Process.js
│ │ ├── Regex.js
│ │ ├── Scheduler.js
│ │ ├── Stream.js
│ │ ├── String.js
│ │ ├── Time.js
│ │ └── Utils.js
├── Json
│ ├── Decode.gren
│ └── Encode.gren
├── Math.gren
├── Maybe.gren
├── Platform.gren
├── Platform
│ ├── Cmd.gren
│ └── Sub.gren
├── Process.gren
├── Random.gren
├── Result.gren
├── Set.gren
├── Stream.gren
├── Stream
│ └── Log.gren
├── String.gren
├── String
│ └── Regex.gren
├── Task.gren
└── Time.gren
└── tests
├── .gitignore
├── gren.json
├── run-tests.sh
└── src
├── Main.gren
└── Test
├── Array.gren
├── ArrayBuilder.gren
├── Basics.gren
├── Bitwise.gren
├── Bytes.gren
├── Char.gren
├── CodeGen.gren
├── Dict.gren
├── Equality.gren
├── Json.gren
├── Math.gren
├── Maybe.gren
├── Regex.gren
├── Result.gren
├── Set.gren
└── String.gren
/.envrc:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Automatically sets up your devbox environment whenever you cd into this
4 | # directory via our direnv integration:
5 |
6 | eval "$(devbox generate direnv --print-envrc)"
7 |
8 | # check out https://www.jetpack.io/devbox/docs/ide_configuration/direnv/
9 | # for more details
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: gren
2 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | pull_request:
5 | branches: [main]
6 | push:
7 | branches: [main]
8 |
9 | jobs:
10 | run-tests:
11 | strategy:
12 | matrix:
13 | os: [ubuntu-latest, macos-latest]
14 |
15 | runs-on: ${{ matrix.os }}
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Install devbox
21 | uses: jetify-com/devbox-install-action@v0.12.0
22 | with:
23 | enable-cache: true
24 |
25 | - name: Verify formatting
26 | run: devbox run format:check
27 |
28 | - name: Run unit tests
29 | run: devbox run test
30 |
31 | - name: Run integration tests
32 | run: devbox run integration
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gren
2 | tests/app
3 | integration_tests/app
4 | tests/.gren
5 | *.dat
6 | doc*.json
7 | *~
8 | .DS_Store
9 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Before starting to work on a feature or bug fix, it might be a good idea
4 | to check if the change you're intending to make is a good fit for this package.
5 |
6 | We use the `Help Wanted` tag on github issues to indicate that a PR would be
7 | welcome. If you cannot find an issue for the change you're intending to make,
8 | head over to our [Zulip](https://gren.zulipchat.com) and start a new topic for your
9 | idea in the `#api-design` streams.
10 |
11 | We like to talk things through before commiting to a change, so that's a good way to go about
12 | suggesting new features. Also, we're a friendly bunch, so don't be afraid to say hi.
13 |
14 | All PRs will be considered, but by going through the above process you increase
15 | your chances for a merge significantly.
16 |
--------------------------------------------------------------------------------
/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | Robin Heggelund Hansen
2 | Atle Wee Førre
3 | Julian Antonielli
4 | Gaute Berge
5 | Alexis Lozano
6 | Ju Liu
7 | William Ashton
8 | Revath S Kumar
9 | Justin Blake
10 | Jeroen Engels
11 | Joey Bright
12 | Axel Baudot
13 | Andrew MacMurray
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Original work Copyright 2014-2022 Evan Czaplicki
2 | Modified work Copyright 2022-present The Gren CONTRIBUTORS
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Core Libraries
2 |
3 | Every Gren project needs this package!
4 |
5 | It provides **basic functionality** like addition and subtraction as well as **data structures** like arrays, dictionaries, and sets.
6 |
7 | > **New to Gren?** Go to [gren-lang.org](https://gren-lang.org) for an overview.
8 |
9 | ## Default Imports
10 |
11 | The modules in this package are so common, that some of them are imported by default in all Gren files. So it is as if every Gren file starts with these imports:
12 |
13 | ```gren
14 | import Basics exposing (..)
15 | import Array exposing (Array)
16 | import Maybe exposing (Maybe(..))
17 | import Result exposing (Result(..))
18 | import String exposing (String)
19 | import Char exposing (Char)
20 |
21 | import Debug
22 |
23 | import Platform exposing (Program)
24 | import Platform.Cmd as Cmd exposing (Cmd)
25 | import Platform.Sub as Sub exposing (Sub)
26 | ```
27 |
28 | The intention is to include things that are both extremely useful and very unlikely to overlap with anything that anyone will ever write in a library. By keeping the set of default imports relatively small, it also becomes easier to use whatever version of `map` suits your fancy. Finally, it makes it easier to figure out where the heck a function is coming from.
29 |
--------------------------------------------------------------------------------
/devbox.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.13.7/.schema/devbox.schema.json",
3 | "packages": [
4 | "nodejs@20",
5 | "github:gren-lang/nix/0.5.4",
6 | "nodePackages.prettier@latest"
7 | ],
8 | "shell": {
9 | "init_hook": [
10 | "echo 'Welcome to devbox!' > /dev/null"
11 | ],
12 | "scripts": {
13 | "format": "prettier -w \"!**/*.json\" .",
14 | "format:check": "prettier -c \"!**/*.json\" .",
15 | "test": [
16 | "cd tests/",
17 | "./run-tests.sh"
18 | ],
19 | "integration": [
20 | "cd integration_tests",
21 | "./run-tests.sh"
22 | ]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/devbox.lock:
--------------------------------------------------------------------------------
1 | {
2 | "lockfile_version": "1",
3 | "packages": {
4 | "github:NixOS/nixpkgs/nixpkgs-unstable": {
5 | "resolved": "github:NixOS/nixpkgs/18dd725c29603f582cf1900e0d25f9f1063dbf11?lastModified=1744536153&narHash=sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg%2FN38%3D"
6 | },
7 | "github:gren-lang/nix/0.5.4": {
8 | "resolved": "github:gren-lang/nix/35601dd5c34adce2bbc6740ccc19a55078eb27be?lastModified=1742812541&narHash=sha256-iJcZAefSQM67bMRlJPEXT0%2FkVUoEmcr9NYTUPLoAO%2FE%3D"
9 | },
10 | "nodePackages.prettier@latest": {
11 | "last_modified": "2025-03-23T05:31:05Z",
12 | "resolved": "github:NixOS/nixpkgs/dd613136ee91f67e5dba3f3f41ac99ae89c5406b#nodePackages.prettier",
13 | "source": "devbox-search",
14 | "version": "3.5.3",
15 | "systems": {
16 | "aarch64-darwin": {
17 | "outputs": [
18 | {
19 | "name": "out",
20 | "path": "/nix/store/ri1x9kwnv6a38lycpx03fm8g1xx5r41s-prettier-3.5.3",
21 | "default": true
22 | }
23 | ],
24 | "store_path": "/nix/store/ri1x9kwnv6a38lycpx03fm8g1xx5r41s-prettier-3.5.3"
25 | },
26 | "aarch64-linux": {
27 | "outputs": [
28 | {
29 | "name": "out",
30 | "path": "/nix/store/dl78racfdkxxmmx971k7malgqyvncdjk-prettier-3.5.3",
31 | "default": true
32 | }
33 | ],
34 | "store_path": "/nix/store/dl78racfdkxxmmx971k7malgqyvncdjk-prettier-3.5.3"
35 | },
36 | "x86_64-darwin": {
37 | "outputs": [
38 | {
39 | "name": "out",
40 | "path": "/nix/store/wnra1n75bxjh1bvhpf3wq9mrm9kgn3j5-prettier-3.5.3",
41 | "default": true
42 | }
43 | ],
44 | "store_path": "/nix/store/wnra1n75bxjh1bvhpf3wq9mrm9kgn3j5-prettier-3.5.3"
45 | },
46 | "x86_64-linux": {
47 | "outputs": [
48 | {
49 | "name": "out",
50 | "path": "/nix/store/q8pacnp434vblp7vh5b769fxdismk2qj-prettier-3.5.3",
51 | "default": true
52 | }
53 | ],
54 | "store_path": "/nix/store/q8pacnp434vblp7vh5b769fxdismk2qj-prettier-3.5.3"
55 | }
56 | }
57 | },
58 | "nodejs@20": {
59 | "last_modified": "2025-03-16T23:54:35Z",
60 | "plugin_version": "0.0.2",
61 | "resolved": "github:NixOS/nixpkgs/5d9b5431f967007b3952c057fc92af49a4c5f3b2#nodejs_20",
62 | "source": "devbox-search",
63 | "version": "20.19.0",
64 | "systems": {
65 | "aarch64-darwin": {
66 | "outputs": [
67 | {
68 | "name": "out",
69 | "path": "/nix/store/4i96rnl7hdzm3k3w36gx7d2p1af3b64q-nodejs-20.19.0",
70 | "default": true
71 | },
72 | {
73 | "name": "libv8",
74 | "path": "/nix/store/dvr6jyyhygpci5yj5mdl3p0xi2qcm02g-nodejs-20.19.0-libv8"
75 | }
76 | ],
77 | "store_path": "/nix/store/4i96rnl7hdzm3k3w36gx7d2p1af3b64q-nodejs-20.19.0"
78 | },
79 | "aarch64-linux": {
80 | "outputs": [
81 | {
82 | "name": "out",
83 | "path": "/nix/store/qz522zmsx441py29xy69sf5671bkhpj2-nodejs-20.19.0",
84 | "default": true
85 | },
86 | {
87 | "name": "libv8",
88 | "path": "/nix/store/7i02830b2pxlapzav6yyggymqmp6rk02-nodejs-20.19.0-libv8"
89 | }
90 | ],
91 | "store_path": "/nix/store/qz522zmsx441py29xy69sf5671bkhpj2-nodejs-20.19.0"
92 | },
93 | "x86_64-darwin": {
94 | "outputs": [
95 | {
96 | "name": "out",
97 | "path": "/nix/store/4k73p2z9h5rnfv9gal8n6m4srx5sggs8-nodejs-20.19.0",
98 | "default": true
99 | },
100 | {
101 | "name": "libv8",
102 | "path": "/nix/store/px89hqr3wcfk0w6146hf4171bvj4chgp-nodejs-20.19.0-libv8"
103 | }
104 | ],
105 | "store_path": "/nix/store/4k73p2z9h5rnfv9gal8n6m4srx5sggs8-nodejs-20.19.0"
106 | },
107 | "x86_64-linux": {
108 | "outputs": [
109 | {
110 | "name": "out",
111 | "path": "/nix/store/088a1y8kvrmvi7vm5ff67hg7c4hcnqg7-nodejs-20.19.0",
112 | "default": true
113 | },
114 | {
115 | "name": "libv8",
116 | "path": "/nix/store/njqqwr5311xmnm05ms3n7b8piwnyq7x1-nodejs-20.19.0-libv8"
117 | }
118 | ],
119 | "store_path": "/nix/store/088a1y8kvrmvi7vm5ff67hg7c4hcnqg7-nodejs-20.19.0"
120 | }
121 | }
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/gren.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "package",
3 | "platform": "common",
4 | "name": "gren-lang/core",
5 | "summary": "Gren's core modules",
6 | "license": "BSD-3-Clause",
7 | "version": "6.0.1",
8 | "exposed-modules": {
9 | "Primitives": [
10 | "Basics",
11 | "String",
12 | "Char",
13 | "Bitwise",
14 | "Math"
15 | ],
16 | "Bytes": [
17 | "Bytes",
18 | "Bytes.Encode",
19 | "Bytes.Decode"
20 | ],
21 | "Collections": [
22 | "Array",
23 | "Array.Builder",
24 | "Dict",
25 | "Set"
26 | ],
27 | "Streams": [
28 | "Stream",
29 | "Stream.Log"
30 | ],
31 | "Error Handling": [
32 | "Maybe",
33 | "Result"
34 | ],
35 | "Debug": [
36 | "Debug"
37 | ],
38 | "Effects": [
39 | "Platform",
40 | "Platform.Cmd",
41 | "Platform.Sub",
42 | "Process",
43 | "Task"
44 | ],
45 | "Json": [
46 | "Json.Decode",
47 | "Json.Encode"
48 | ],
49 | "Random": [
50 | "Random"
51 | ],
52 | "Strings": [
53 | "String.Regex"
54 | ],
55 | "Time": [
56 | "Time"
57 | ],
58 | "Crypto": [
59 | "Crypto"
60 | ]
61 | },
62 | "gren-version": "0.5.0 <= v < 0.6.0",
63 | "dependencies": {}
64 | }
65 |
--------------------------------------------------------------------------------
/hints/0.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | Your application is triggering a bug in the `Dict` module of this library! The root cause may be in your code or in a dependency.
12 |
13 | Try to reproduce the issue in dev mode. From there, create an [SSCCE](http://sscce.org/) that reproduces the issue and report it [here](https://github.com/elm-lang/core/issues) with a title like “Dict bug: ...” and a description that explains your understanding of the problem.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/1.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | The application transitioned to a new URL, but the `Url.Parser.toUrl` function could not handle it. It probably is an unsupported URL like a `file://` or `data://` URL but it may be a bug in the [`elm-lang/url`][url] package. Explore more and report an [SSCCE](http://sscce.org/) if you can definitively show it is a bug.
12 |
13 | You can get more detailed information if you can reproduce the error in dev mode.
14 |
15 | Hopefully that helps!
16 |
17 | [url]: https://github.com/elm-lang/url
18 |
--------------------------------------------------------------------------------
/hints/10.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
--------------------------------------------------------------------------------
/hints/11.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | Your application is calling `modBy 0 n` at some point, causing a crash. This may be happening in your code or in a dependency.
12 |
13 | You can get more detailed information about when this happens if you can reproduce this error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/2.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | When you initialize your Elm application, you are providing bad flags.
12 |
13 | You can get more detailed information if you can reproduce the error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/3.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | Your application has multiple ports with the same name.
12 |
13 | You can get more detailed information if you can reproduce the error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/4.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | You are trying to send an invalid value through a port.
12 |
13 | You can get more detailed information if you can reproduce the error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/5.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | Trying to use `(==)` on functions. There is no way to know if functions are "the same" in the Elm sense. You can read more about why it works this way [here](https://package.elm-lang.org/packages/elm-lang/core/latest/Basics#==). The root cause may be in your code or in a dependency.
12 |
13 | You can get more detailed information on when this happens if you can reproduce the error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/6.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | This page loads multiple Elm scripts. It then puts the publicly exposed modules from each script on the global `Elm` object. The problem is that there is a name clash.
12 |
13 | For example, script `a.js` may expose an Elm module named `Main`, and script `b.js` may expose an Elm module named `Main`. Elm cannot know which one to use! So they need to have unique names if you need them to be added to the global `Elm` object.
14 |
15 | Maybe a duplicate script is getting loaded accidentally? Anyway, if you reproduce this error in dev mode, it will tell you the module name that is getting added more than once.
16 |
17 | Hopefully that helps!
18 |
--------------------------------------------------------------------------------
/hints/8.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | Your application is calling `Debug.crash` at some point. The call may be in your code or in a dependency.
12 |
13 | You can get more detailed information on who is calling `Debug.crash` if you can reproduce this error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/hints/9.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 | ## Info for Developers
10 |
11 | Your application is calling `Debug.crash` in a `case` at some point. The call may be in your code or in a dependency.
12 |
13 | You can get more detailed information on who is calling `Debug.crash` if you can reproduce this error in dev mode.
14 |
15 | Hopefully that helps!
16 |
--------------------------------------------------------------------------------
/integration_tests/gren.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "application",
3 | "platform": "node",
4 | "source-directories": [
5 | "src"
6 | ],
7 | "gren-version": "0.5.3",
8 | "dependencies": {
9 | "direct": {
10 | "blaix/gren-effectful-tests-node": "4.0.0",
11 | "gren-lang/core": "local:../",
12 | "gren-lang/node": "5.0.0",
13 | "gren-lang/test": "4.1.0",
14 | "gren-lang/test-runner-node": "6.0.0"
15 | },
16 | "indirect": {
17 | "gren-lang/url": "5.0.0"
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/integration_tests/run-tests.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | gren make src/Main.gren
4 | node app
5 |
--------------------------------------------------------------------------------
/integration_tests/src/Main.gren:
--------------------------------------------------------------------------------
1 | module Main exposing ( .. )
2 |
3 |
4 | {-|-}
5 |
6 | import Test.Crypto as Crypto
7 | import Test.Task as Task
8 | import Test.Runner.Effectful exposing (concat)
9 | import Node
10 |
11 |
12 | {-| A set of effectful tests that must be run in a different `Program` type than the main tests.
13 | -}
14 | main : Test.Runner.Effectful.Program a
15 | main =
16 | Node.defineSimpleProgram (\env ->
17 | Test.Runner.Effectful.run env
18 | (concat
19 | [ Crypto.tests
20 | , Task.tests
21 | ]
22 | )
23 | )
24 |
--------------------------------------------------------------------------------
/src/Array/Builder.gren:
--------------------------------------------------------------------------------
1 | module Array.Builder exposing
2 | ( Builder
3 | , empty, fromArray
4 | , pushLast, append
5 | , toArray
6 | )
7 |
8 | {-| The functions in this module allows you to build an `Array` more efficiently.
9 | Unless you have a performance problem, it's recommended to stick with the functions
10 | in the `Array` module.
11 |
12 | @docs Builder, empty, fromArray, pushLast, append, toArray
13 |
14 | -}
15 |
16 | import Array exposing (Array)
17 | import Basics exposing (Int, clamp)
18 | import Gren.Kernel.Array
19 | import Random
20 |
21 |
22 | {-| A `Builder` represents an `Array` under construction. The functionality
23 | of this type is intentionally limited. You can only create it, add elements to
24 | it and convert it to an `Array`. Any other operation is out of scope. In return,
25 | you get a faster way of building `Array`s.
26 | -}
27 | type Builder a =
28 | -- NOTE: Actual implementation in kernel code
29 | Builder
30 |
31 |
32 | {-| Creates a [Builder](#Builder) without any elements.
33 |
34 | The `Int` argument lets you specify the amount of elements you expect
35 | to end up in the final `Array`. Nothing bad happens if you pass a wrong
36 | value, but being exact gives you a tiny bit more performance.
37 |
38 | In other words: it's ok to pass `0`.
39 | -}
40 | empty : Int -> Builder a
41 | empty capacity =
42 | let
43 | clampedCapacity =
44 | clamp 0 Random.maxInt capacity
45 | in
46 | Gren.Kernel.Array.emptyBuilder clampedCapacity
47 |
48 |
49 | {-| Creates a [Builder](#Builder) based on an existing `Array`.
50 | -}
51 | fromArray : Array a -> Builder a
52 | fromArray =
53 | Gren.Kernel.Array.toBuilder
54 |
55 |
56 | {-| Add a single element to a [Builder](#Builder).
57 | -}
58 | pushLast : a -> Builder a -> Builder a
59 | pushLast =
60 | Gren.Kernel.Array.pushToBuilder
61 |
62 |
63 | {-| Adds the elements of an `Array` to the end of a [Builder](#Builder).
64 | -}
65 | append : Array a -> Builder a -> Builder a
66 | append =
67 | Gren.Kernel.Array.appendToBuilder
68 |
69 |
70 | {-| Converts a [Builder](#Builder) to an `Array`.
71 | -}
72 | toArray : Builder a -> Array a
73 | toArray =
74 | Gren.Kernel.Array.fromBuilder
75 |
--------------------------------------------------------------------------------
/src/Bitwise.gren:
--------------------------------------------------------------------------------
1 | module Bitwise exposing
2 | ( and, or, xor, complement, countLeadingZeros
3 | , shiftLeftBy, shiftRightBy, shiftRightZfBy
4 | )
5 |
6 | {-| Functions for doing [bitwise operations](https://en.wikipedia.org/wiki/Bitwise_operation).
7 |
8 | Bitwise operations only work on 32-bit integers.
9 | When an `Int` is passed to a bitwise operation, only the 32 least significant bits are used.
10 |
11 | @docs and, or, xor, complement, countLeadingZeros
12 |
13 |
14 | ## Bit Shifts
15 |
16 | Note: Because bitwise operations only work on 32-bit integers, the shift distance is effectively limited to 31.
17 | If you shift by `n`, only the lowest 5 bits of `n` will be used and the binary will be shifted by `modBy 32 n`.
18 | For example, the following two commands perform the same operation:
19 |
20 | Bitwise.shiftLeftBy 1 10
21 | Bitwise.shiftLeftBy 33 10
22 |
23 |
24 | @docs shiftLeftBy, shiftRightBy, shiftRightZfBy
25 |
26 | -}
27 |
28 | import Basics exposing (Int)
29 | import Gren.Kernel.Bitwise
30 |
31 |
32 | {-| Bitwise AND
33 | -}
34 | and : Int -> Int -> Int
35 | and =
36 | Gren.Kernel.Bitwise.and
37 |
38 |
39 | {-| Bitwise OR
40 | -}
41 | or : Int -> Int -> Int
42 | or =
43 | Gren.Kernel.Bitwise.or
44 |
45 |
46 | {-| Bitwise XOR
47 | -}
48 | xor : Int -> Int -> Int
49 | xor =
50 | Gren.Kernel.Bitwise.xor
51 |
52 |
53 | {-| Flip each bit individually, often called bitwise NOT
54 | -}
55 | complement : Int -> Int
56 | complement =
57 | Gren.Kernel.Bitwise.complement
58 |
59 | {-| Count the number of leading zero bits in an integer.
60 |
61 | countLeadingZeros 0 == 32
62 | countLeadingZeros 1 == 31
63 | countLeadingZeros 0xFFFF == 16
64 |
65 | -}
66 | countLeadingZeros : Int -> Int
67 | countLeadingZeros =
68 | Gren.Kernel.Bitwise.countLeadingZeros
69 |
70 |
71 | {-| Shift bits to the left by a given offset, filling new bits with zeros.
72 | This can be used to multiply numbers by powers of two.
73 |
74 | shiftLeftBy 1 5 == 10
75 |
76 | shiftLeftBy 5 1 == 32
77 |
78 | -}
79 | shiftLeftBy : Int -> Int -> Int
80 | shiftLeftBy =
81 | Gren.Kernel.Bitwise.shiftLeftBy
82 |
83 |
84 | {-| Shift bits to the right by a given offset, filling new bits with
85 | whatever is the topmost bit. This can be used to divide numbers by powers of two.
86 |
87 | shiftRightBy 1 32 == 16
88 |
89 | shiftRightBy 2 32 == 8
90 |
91 | shiftRightBy 1 -32 == -16
92 |
93 | This is called an [arithmetic right shift][ars], often written `>>`, and
94 | sometimes called a sign-propagating right shift because it fills empty spots
95 | with copies of the highest bit.
96 |
97 | [ars]: https://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift
98 |
99 | -}
100 | shiftRightBy : Int -> Int -> Int
101 | shiftRightBy =
102 | Gren.Kernel.Bitwise.shiftRightBy
103 |
104 |
105 | {-| Shift bits to the right by a given offset, filling new bits with zeros.
106 |
107 | shiftRightZfBy 1 32 == 16
108 |
109 | shiftRightZfBy 2 32 == 8
110 |
111 | shiftRightZfBy 1 -32 == 2147483632
112 |
113 | This is called an [logical right shift][lrs], often written `>>>`, and
114 | sometimes called a zero-fill right shift because it fills empty spots with
115 | zeros.
116 |
117 | [lrs]: https://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift
118 |
119 | -}
120 | shiftRightZfBy : Int -> Int -> Int
121 | shiftRightZfBy =
122 | Gren.Kernel.Bitwise.shiftRightZfBy
123 |
--------------------------------------------------------------------------------
/src/Bytes.gren:
--------------------------------------------------------------------------------
1 | module Bytes exposing
2 | ( Bytes
3 | , empty
4 | , isEmpty
5 | , length
6 | --
7 | , Endianness(..)
8 | , getHostEndianness
9 | --
10 | , fromString
11 | , toString
12 | --
13 | , flatten
14 | )
15 |
16 |
17 | {-| Functions for working with sequences of bytes.
18 |
19 | @docs Bytes, empty, isEmpty, length
20 |
21 | ## Endianness
22 | @docs Endianness, getHostEndianness
23 |
24 | ## Strings
25 | @docs fromString, toString
26 |
27 | ## Combine
28 | @docs flatten
29 |
30 | -}
31 |
32 |
33 | import Array exposing (Array)
34 | import Basics exposing (..)
35 | import Maybe exposing (Maybe)
36 | import String exposing (String)
37 | import Task exposing (Task)
38 | import Gren.Kernel.Bytes
39 |
40 |
41 | -- BYTES
42 |
43 |
44 | {-| A sequence of bytes.
45 |
46 | A byte is a chunk of eight bits. For example, the letter `j` is usually
47 | represented as the byte `01101010`, and the letter `k` is `01101011`.
48 |
49 | Seeing each byte as a stream of zeros and ones can be quite confusing though,
50 | so it is common to use hexidecimal numbers instead:
51 |
52 | ```
53 | | Binary | Hex |
54 | +--------+-----+
55 | | 0000 | 0 |
56 | | 0001 | 1 |
57 | | 0010 | 2 |
58 | | 0011 | 3 | j = 01101010
59 | | 0100 | 4 | \__/\__/
60 | | 0101 | 5 | | |
61 | | 0110 | 6 | 6 A
62 | | 0111 | 7 |
63 | | 1000 | 8 | k = 01101011
64 | | 1001 | 9 | \__/\__/
65 | | 1010 | A | | |
66 | | 1011 | B | 6 B
67 | | 1100 | C |
68 | | 1101 | D |
69 | | 1110 | E |
70 | | 1111 | F |
71 | ```
72 |
73 | So `j` is `6A` and `k` is `6B` in hexidecimal. This more compact representation
74 | is great when you have a sequence of bytes. You can see this even in a short
75 | string like `"jazz"`:
76 |
77 | ```
78 | binary hexidecimal
79 | 01101010 01100001 01111010 01111010 => 6A 61 7A 7A
80 | ```
81 |
82 | Anyway, the point is that `Bytes` is a sequence of bytes!
83 | -}
84 | type Bytes = Bytes
85 |
86 |
87 | {-| The sequence with exactly 0 bytes.
88 | -}
89 | empty : Bytes
90 | empty =
91 | Gren.Kernel.Bytes.empty
92 |
93 |
94 | {-| Check if the given byte sequence has 0 bytes in it.
95 | -}
96 | isEmpty : Bytes -> Bool
97 | isEmpty bytes =
98 | length bytes == 0
99 |
100 |
101 | {-| Get the length of a sequence of bytes.
102 |
103 | So if a sequence has four-hundred bytes, then `length bytes` would give back
104 | `400`. That may be 400 unsigned 8-bit integers, 100 signed 32-bit integers, or
105 | even a UTF-8 string. The content does not matter. This is just figuring out
106 | how many bytes there are!
107 | -}
108 | length : Bytes -> Int
109 | length =
110 | Gren.Kernel.Bytes.length
111 |
112 |
113 |
114 | -- ENDIANNESS
115 |
116 |
117 | {-| Different computers store integers and floats slightly differently in
118 | memory. Say we have the integer `0x1A2B3C4D` in our program. It needs four
119 | bytes (32 bits) in memory. It may seem reasonable to lay them out in order:
120 |
121 | ```
122 | Big-Endian (BE) (Obvious Order)
123 | +----+----+----+----+
124 | | 1A | 2B | 3C | 4D |
125 | +----+----+----+----+
126 | ```
127 |
128 | But some people thought it would be better to store the bytes in the opposite
129 | order:
130 |
131 | ```
132 | Little-Endian (LE) (Shuffled Order)
133 | +----+----+----+----+
134 | | 4D | 3C | 2B | 1A |
135 | +----+----+----+----+
136 | ```
137 |
138 | Notice that **the _bytes_ are shuffled, not the bits.** It is like if you cut a
139 | photo into four strips and shuffled the strips. It is not a mirror image.
140 | The theory seems to be that an 8-bit `0x1A` and a 32-bit `0x0000001A` both have
141 | `1A` as the first byte in this scheme. Maybe this was helpful when processors
142 | handled one byte at a time.
143 |
144 | **Most processors use little-endian (LE) layout.** This seems to be because
145 | Intel did it this way, and other chip manufactures followed their convention.
146 | **Most network protocols use big-endian (BE) layout.** I suspect this is
147 | because if you are trying to debug a network protocol, it is nice if your
148 | integers are not all shuffled.
149 |
150 | **Note:** Endianness is relevant for integers and floats, but not strings.
151 | UTF-8 specifies the order of bytes explicitly.
152 |
153 | **Note:** The terms little-endian and big-endian are a reference to an egg joke
154 | in Gulliver's Travels. They first appeared in 1980 in [this essay][essay], and
155 | you can decide for yourself if they stood the test of time. I personally find
156 | these terms quite unhelpful, so I say “Obvious Order” and “Shuffled Order” in
157 | my head. I remember which is more common by asking myself, “if things were
158 | obvious, would I have to ask this question?”
159 |
160 | [essay]: http://www.ietf.org/rfc/ien/ien137.txt
161 | -}
162 | type Endianness = LE | BE
163 |
164 |
165 | {-| Is this program running on a big-endian or little-endian machine?
166 | -}
167 | getHostEndianness : Task x Endianness
168 | getHostEndianness =
169 | Gren.Kernel.Bytes.getHostEndianness LE BE
170 |
171 |
172 | -- STRINGS
173 |
174 |
175 | {-| Convert a `String` to a `Bytes`. The resulting bytes will be in the
176 | UTF-8 encoding.
177 |
178 | Some characters take one byte, while others can take up to four. Read more
179 | about [UTF-8](https://en.wikipedia.org/wiki/UTF-8) to learn the details!
180 | -}
181 | fromString : String -> Bytes
182 | fromString =
183 | Gren.Kernel.Bytes.fromString
184 |
185 |
186 | {-| Convert UTF-8 encoded `Bytes` to `String`. If the byte sequence isn't
187 | valid UTF-8, `Nothing` will be returned.
188 | -}
189 | toString : Bytes -> Maybe String
190 | toString =
191 | Gren.Kernel.Bytes.toString
192 |
193 |
194 | -- COMBINE
195 |
196 |
197 | {-| Flatten all `Bytes` in an `Array` into a single `Bytes`.
198 | -}
199 | flatten : Array Bytes -> Bytes
200 | flatten =
201 | Gren.Kernel.Bytes.flatten
202 |
--------------------------------------------------------------------------------
/src/Bytes/Encode.gren:
--------------------------------------------------------------------------------
1 | module Bytes.Encode exposing
2 | ( encode
3 | , Encoder
4 | , signedInt8, signedInt16, signedInt32
5 | , unsignedInt8, unsignedInt16, unsignedInt32
6 | , float32, float64
7 | , bytes
8 | , sequence
9 | )
10 |
11 |
12 | {-| Functions for turning things into bytes.
13 |
14 | @docs Encoder, encode, sequence
15 |
16 | ## Integers
17 | @docs signedInt8, signedInt16, signedInt32, unsignedInt8, unsignedInt16, unsignedInt32
18 |
19 | ## Floats
20 | @docs float32, float64
21 |
22 | ## Bytes
23 | @docs bytes
24 |
25 | -}
26 |
27 |
28 | import Array exposing (Array)
29 | import Basics exposing (..)
30 | import Bytes exposing (Bytes, Endianness(..))
31 | import Maybe exposing (Maybe(..))
32 | import String exposing (String)
33 |
34 |
35 |
36 | -- ENCODER
37 |
38 |
39 | {-| Describes how to generate a sequence of bytes.
40 |
41 | These encoders snap together with [`sequence`](#sequence) so you can start with
42 | small building blocks and put them together into a more complex encoding.
43 | -}
44 | type Encoder
45 | = I8 Int
46 | | I16 (WithEndian Int)
47 | | I32 (WithEndian Int)
48 | | U8 Int
49 | | U16 (WithEndian Int)
50 | | U32 (WithEndian Int)
51 | | F32 (WithEndian Float)
52 | | F64 (WithEndian Float)
53 | | Seq { width : Int, items : Array Encoder }
54 | | Bytes Bytes
55 |
56 |
57 | type alias WithEndian number =
58 | { endian : Endianness
59 | , number : number
60 | }
61 |
62 |
63 | -- ENCODE
64 |
65 |
66 | {-| Turn an `Encoder` into `Bytes`.
67 |
68 | encode (unsignedInt8 7) -- <07>
69 | encode (unsignedInt16 BE 7) -- <0007>
70 | encode (unsignedInt16 LE 7) -- <0700>
71 |
72 | The `encode` function is designed to minimize allocation. It figures out the
73 | exact length necessary to fit everything in `Bytes` and then generate that
74 | value directly. This is valuable when you are encoding more elaborate data:
75 |
76 | import Bytes exposing (Endianness(..))
77 | import Bytes.Encode as Encode
78 |
79 | type alias Person =
80 | { age : Int
81 | , name : String
82 | }
83 |
84 | toEncoder : Person -> Encode.Encoder
85 | toEncoder person =
86 | Encode.sequence
87 | [ Encode.unsignedInt16 BE person.age
88 | , Encode.unsignedInt16 BE (Encode.getStringWidth person.name)
89 | , Encode.string person.name
90 | ]
91 |
92 | -- encode (toEncoder ({ age = 33, name = "Tom" })) == <00210003546F6D>
93 |
94 | Did you know it was going to be seven bytes? How about when you have a hundred
95 | people to serialize? And when some have Japanese and Norwegian names? Having
96 | this intermediate `Encoder` can help reduce allocation quite a lot!
97 | -}
98 | encode : Encoder -> Bytes
99 | encode =
100 | Gren.Kernel.Bytes.encode
101 |
102 |
103 |
104 | -- INTEGERS
105 |
106 |
107 | {-| Encode integers from `-128` to `127` in one byte.
108 | -}
109 | signedInt8 : Int -> Encoder
110 | signedInt8 n =
111 | I8 n
112 |
113 |
114 | {-| Encode integers from `-32768` to `32767` in two bytes.
115 | -}
116 | signedInt16 : Endianness -> Int -> Encoder
117 | signedInt16 e n =
118 | I16 { endian = e, number = n }
119 |
120 |
121 | {-| Encode integers from `-2147483648` to `2147483647` in four bytes.
122 | -}
123 | signedInt32 : Endianness -> Int -> Encoder
124 | signedInt32 e n =
125 | I32 { endian = e, number = n }
126 |
127 |
128 | {-| Encode integers from `0` to `255` in one byte.
129 | -}
130 | unsignedInt8 : Int -> Encoder
131 | unsignedInt8 n =
132 | U8 n
133 |
134 |
135 | {-| Encode integers from `0` to `65535` in two bytes.
136 | -}
137 | unsignedInt16 : Endianness -> Int -> Encoder
138 | unsignedInt16 e n =
139 | U16 { endian = e, number = n }
140 |
141 |
142 | {-| Encode integers from `0` to `4294967295` in four bytes.
143 | -}
144 | unsignedInt32 : Endianness -> Int -> Encoder
145 | unsignedInt32 e n =
146 | U32 { endian = e, number = n }
147 |
148 |
149 | -- FLOATS
150 |
151 |
152 | {-| Encode 32-bit floating point numbers in four bytes.
153 | -}
154 | float32 : Endianness -> Float -> Encoder
155 | float32 e n =
156 | F32 { endian = e, number = n }
157 |
158 |
159 | {-| Encode 64-bit floating point numbers in eight bytes.
160 | -}
161 | float64 : Endianness -> Float -> Encoder
162 | float64 e n =
163 | F64 { endian = e, number = n }
164 |
165 |
166 |
167 | -- BYTES
168 |
169 |
170 | {-| Copy bytes directly into the new `Bytes` sequence. This does not record the
171 | length though! You usually want to say something like this:
172 |
173 | import Bytes exposing (Bytes, Endianness(..))
174 | import Bytes.Encode as Encode
175 |
176 | png : Bytes -> Encode.Encoder
177 | png imageData =
178 | Encode.sequence
179 | [ Encode.unsignedInt32 BE (Bytes.length imageData)
180 | , Encode.bytes imageData
181 | ]
182 |
183 | This allows you to represent the length however is necessary for your protocol.
184 | For example, you can use [Base 128 Varints][pb] for ProtoBuf,
185 | [Variable-Length Integers][sql] for SQLite, or whatever else they dream up.
186 |
187 | [pb]: https://developers.google.com/protocol-buffers/docs/encoding#varints
188 | [sql]: https://www.sqlite.org/src4/doc/trunk/www/varint.wiki
189 | -}
190 | bytes : Bytes -> Encoder
191 | bytes =
192 | Bytes
193 |
194 |
195 | -- SEQUENCE
196 |
197 |
198 | {-| Put together a bunch of builders. So if you wanted to encode three `Float`
199 | values for the position of a ball in 3D space, you could say:
200 |
201 | import Bytes exposing (Endianness(..))
202 | import Bytes.Encode as Encode
203 |
204 | type alias Ball = { x : Float, y : Float, z : Float }
205 |
206 | ball : Ball -> Encode.Encoder
207 | ball {x,y,z} =
208 | Encode.sequence
209 | [ Encode.float32 BE x
210 | , Encode.float32 BE y
211 | , Encode.float32 BE z
212 | ]
213 |
214 | -}
215 | sequence : Array Encoder -> Encoder
216 | sequence builders =
217 | Seq { width = (getLengths 0 builders), items = builders }
218 |
219 |
220 | -- WRITE
221 |
222 |
223 | write : Encoder -> Bytes -> Int -> Int
224 | write builder mb offset =
225 | when builder is
226 | I8 n -> Gren.Kernel.Bytes.write_i8 mb offset n
227 | I16 { endian = e, number = n } -> Gren.Kernel.Bytes.write_i16 mb offset n (e == LE)
228 | I32 { endian = e, number = n } -> Gren.Kernel.Bytes.write_i32 mb offset n (e == LE)
229 | U8 n -> Gren.Kernel.Bytes.write_u8 mb offset n
230 | U16 { endian = e, number = n } -> Gren.Kernel.Bytes.write_u16 mb offset n (e == LE)
231 | U32 { endian = e, number = n } -> Gren.Kernel.Bytes.write_u32 mb offset n (e == LE)
232 | F32 { endian = e, number = n } -> Gren.Kernel.Bytes.write_f32 mb offset n (e == LE)
233 | F64 { endian = e, number = n } -> Gren.Kernel.Bytes.write_f64 mb offset n (e == LE)
234 | Seq { items = bs } -> writeSequence bs mb offset
235 | Bytes bs -> Gren.Kernel.Bytes.write_bytes mb offset bs
236 |
237 |
238 | writeSequence : Array Encoder -> Bytes -> Int -> Int
239 | writeSequence builders mb offset =
240 | Array.foldl
241 | (\builder currentOffset ->
242 | write builder mb currentOffset
243 | )
244 | offset
245 | builders
246 |
247 |
248 | -- LENGTHS
249 |
250 |
251 | getLength : Encoder -> Int
252 | getLength builder =
253 | when builder is
254 | I8 _ -> 1
255 | I16 _ -> 2
256 | I32 _ -> 4
257 | U8 _ -> 1
258 | U16 _ -> 2
259 | U32 _ -> 4
260 | F32 _ -> 4
261 | F64 _ -> 8
262 | Seq { width = w } -> w
263 | Bytes bs -> Gren.Kernel.Bytes.length bs
264 |
265 |
266 | getLengths : Int -> Array Encoder -> Int
267 | getLengths length builders =
268 | Array.foldl
269 | (\builder sum ->
270 | sum + getLength builder
271 | )
272 | length
273 | builders
274 |
--------------------------------------------------------------------------------
/src/Char.gren:
--------------------------------------------------------------------------------
1 | module Char exposing
2 | ( Char
3 | , isUpper, isLower, isAlpha, isAlphaNum
4 | , isDigit, isOctDigit, isHexDigit
5 | , toCode, fromCode
6 | )
7 |
8 | {-| Functions for working with characters. Character literals are enclosed in
9 | `'a'` pair of single quotes.
10 |
11 |
12 | @docs Char
13 |
14 |
15 | ## ASCII Letters
16 |
17 | @docs isUpper, isLower, isAlpha, isAlphaNum
18 |
19 |
20 | ## Digits
21 |
22 | @docs isDigit, isOctDigit, isHexDigit
23 |
24 |
25 | ## Unicode Code Points
26 |
27 | @docs toCode, fromCode
28 |
29 | -}
30 |
31 | import Basics exposing ((&&), (<=), (>=), (||), Bool, Int)
32 | import Gren.Kernel.Char
33 |
34 |
35 |
36 | -- CHAR
37 |
38 |
39 | {-| A `Char` is a single [unicode][u] character:
40 |
41 | 'a'
42 |
43 | '0'
44 |
45 | 'Z'
46 |
47 | '?'
48 |
49 | '"'
50 |
51 | 'Σ'
52 |
53 | '🙈'
54 |
55 | '\t'
56 |
57 | '"'
58 |
59 | '\''
60 |
61 | '🙈' -- '🙈'
62 |
63 | **Note 1:** You _cannot_ use single quotes around multiple characters like in
64 | JavaScript. This is how we distinguish [`String`](String#String) and `Char`
65 | values in syntax.
66 |
67 | **Note 2:** You can use the unicode escapes from `\u{0000}` to `\u{10FFFF}` to
68 | represent characters by their code point. You can also include the unicode
69 | characters directly. Using the escapes can be better if you need one of the
70 | many whitespace characters with different widths.
71 |
72 | [u]: https://en.wikipedia.org/wiki/Unicode
73 |
74 | -}
75 | type Char
76 | = Char -- NOTE: The compiler provides the real implementation.
77 |
78 |
79 |
80 | -- CLASSIFICATION
81 |
82 |
83 | {-| Detect upper case ASCII characters.
84 |
85 | isUpper 'A' == True
86 |
87 | isUpper 'B'
88 | == True
89 | ... isUpper 'Z'
90 | == True
91 |
92 | isUpper '0' == False
93 |
94 | isUpper 'a' == False
95 |
96 | isUpper '-' == False
97 |
98 | isUpper 'Σ' == False
99 |
100 | -}
101 | isUpper : Char -> Bool
102 | isUpper char =
103 | let
104 | code =
105 | toCode char
106 | in
107 | code <= 0x5A && 0x41 <= code
108 |
109 |
110 | {-| Detect lower case ASCII characters.
111 |
112 | isLower 'a' == True
113 |
114 | isLower 'b'
115 | == True
116 | ... isLower 'z'
117 | == True
118 |
119 | isLower '0' == False
120 |
121 | isLower 'A' == False
122 |
123 | isLower '-' == False
124 |
125 | isLower 'π' == False
126 |
127 | -}
128 | isLower : Char -> Bool
129 | isLower char =
130 | let
131 | code =
132 | toCode char
133 | in
134 | 0x61 <= code && code <= 0x7A
135 |
136 |
137 | {-| Detect upper case and lower case ASCII characters.
138 |
139 | isAlpha 'a' == True
140 |
141 | isAlpha 'b' == True
142 |
143 | isAlpha 'E' == True
144 |
145 | isAlpha 'Y' == True
146 |
147 | isAlpha '0' == False
148 |
149 | isAlpha '-' == False
150 |
151 | isAlpha 'π' == False
152 |
153 | -}
154 | isAlpha : Char -> Bool
155 | isAlpha char =
156 | isLower char || isUpper char
157 |
158 |
159 | {-| Detect upper case and lower case ASCII characters.
160 |
161 | isAlphaNum 'a' == True
162 |
163 | isAlphaNum 'b' == True
164 |
165 | isAlphaNum 'E' == True
166 |
167 | isAlphaNum 'Y' == True
168 |
169 | isAlphaNum '0' == True
170 |
171 | isAlphaNum '7' == True
172 |
173 | isAlphaNum '-' == False
174 |
175 | isAlphaNum 'π' == False
176 |
177 | -}
178 | isAlphaNum : Char -> Bool
179 | isAlphaNum char =
180 | isLower char || isUpper char || isDigit char
181 |
182 |
183 | {-| Detect digits `0123456789`
184 |
185 | isDigit '0' == True
186 |
187 | isDigit '1'
188 | == True
189 | ... isDigit '9'
190 | == True
191 |
192 | isDigit 'a' == False
193 |
194 | isDigit 'b' == False
195 |
196 | isDigit 'A' == False
197 |
198 | -}
199 | isDigit : Char -> Bool
200 | isDigit char =
201 | let
202 | code =
203 | toCode char
204 | in
205 | code <= 0x39 && 0x30 <= code
206 |
207 |
208 | {-| Detect octal digits `01234567`
209 |
210 | isOctDigit '0' == True
211 |
212 | isOctDigit '1'
213 | == True
214 | ... isOctDigit '7'
215 | == True
216 |
217 | isOctDigit '8' == False
218 |
219 | isOctDigit 'a' == False
220 |
221 | isOctDigit 'A' == False
222 |
223 | -}
224 | isOctDigit : Char -> Bool
225 | isOctDigit char =
226 | let
227 | code =
228 | toCode char
229 | in
230 | code <= 0x37 && 0x30 <= code
231 |
232 |
233 | {-| Detect hexadecimal digits `0123456789abcdefABCDEF`
234 | -}
235 | isHexDigit : Char -> Bool
236 | isHexDigit char =
237 | let
238 | code =
239 | toCode char
240 | in
241 | (0x30 <= code && code <= 0x39)
242 | || (0x41 <= code && code <= 0x46)
243 | || (0x61 <= code && code <= 0x66)
244 |
245 |
246 |
247 | -- CONVERSIONS
248 |
249 |
250 | {-| Convert to the corresponding Unicode [code point][cp].
251 |
252 | [cp]: https://en.wikipedia.org/wiki/Code_point
253 |
254 | toCode 'A' == 65
255 |
256 | toCode 'B' == 66
257 |
258 | toCode '木' == 0x6728
259 |
260 | toCode '𝌆' == 0x0001D306
261 |
262 | toCode '😃' == 0x0001F603
263 |
264 | -}
265 | toCode : Char -> Int
266 | toCode =
267 | Gren.Kernel.Char.toCode
268 |
269 |
270 | {-| Convert a Unicode [code point][cp] to a character.
271 |
272 | fromCode 65 == 'A'
273 |
274 | fromCode 66 == 'B'
275 |
276 | fromCode 0x6728 == '木'
277 |
278 | fromCode 0x0001D306 == '𝌆'
279 |
280 | fromCode 0x0001F603 == '😃'
281 |
282 | fromCode -1 == '�'
283 |
284 | The full range of unicode is from `0` to `0x10FFFF`. With numbers outside that
285 | range, you get [the replacement character][fffd].
286 |
287 | [cp]: https://en.wikipedia.org/wiki/Code_point
288 | [fffd]: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character
289 |
290 | -}
291 | fromCode : Int -> Char
292 | fromCode =
293 | Gren.Kernel.Char.fromCode
294 |
--------------------------------------------------------------------------------
/src/Debug.gren:
--------------------------------------------------------------------------------
1 | module Debug exposing (toString, log, todo)
2 |
3 | {-| This module can be useful while _developing_ an application. It is not
4 | available for use in packages or production.
5 |
6 | @docs toString, log, todo
7 |
8 | -}
9 |
10 | import Gren.Kernel.Debug
11 | import String exposing (String)
12 |
13 |
14 | {-| Turn any kind of value into a string.
15 |
16 | toString 42 == "42"
17 |
18 | toString [ 1, 2 ] == "[1,2]"
19 |
20 | toString ( 'a', "cat", 13 ) == "('a', \"cat\", 13)"
21 |
22 | toString "he said, \"hi\"" == "\"he said, \\\"hi\\\"\""
23 |
24 | Notice that with strings, this is not the `identity` function. It escapes
25 | characters so if you say `Html.text (toString "he said, \"hi\"")` it will
26 | show `"he said, \"hi\""` rather than `he said, "hi"`. This makes it nice
27 | for viewing Gren data structures.
28 |
29 | **Note:** This is not available with `gren make --optimize` which gets rid of
30 | a bunch of runtime metadata. For example, it shortens record field names, and
31 | we need that info to `toString` the value! As a consequence, packages cannot
32 | use `toString` because they may be used in `--optimize` mode.
33 |
34 | -}
35 | toString : a -> String
36 | toString =
37 | Gren.Kernel.Debug.toString
38 |
39 |
40 | {-| Log a tagged value on the developer console, and then return the value.
41 |
42 | 1 + log "number" 1 -- equals 2, logs "number: 1"
43 |
44 | length (log "start" []) -- equals 0, logs "start: []"
45 |
46 | It is often possible to sprinkle this around to see if values are what you
47 | expect. It is kind of old-school to do it this way, but it works!
48 |
49 | **Note:** This is not available with `gren make --optimize` because (1) it
50 | relies on `toString` which has the same restriction and (2) it is not a pure
51 | function and would therefore have unpredictable behavior when paired with
52 | compiler optimizations that move code around.
53 |
54 | **Note:** If you want to create a terminal application that prints stuff out,
55 | use ports for now. That will give you full access to reading and writing in the
56 | terminal. We may have a package in Gren for this someday, but browser
57 | applications are the primary focus of platform development for now.
58 |
59 | -}
60 | log : String -> a -> a
61 | log =
62 | Gren.Kernel.Debug.log
63 |
64 |
65 | {-| This is a placeholder for code that you will write later.
66 |
67 | For example, if you are working with a large union type and have partially
68 | completed a case expression, it may make sense to do this:
69 |
70 | type Entity = Ship | Fish | Captain | Seagull
71 |
72 | drawEntity entity =
73 | case entity of
74 | Ship ->
75 | ...
76 |
77 | Fish ->
78 | ...
79 |
80 | _ ->
81 | Debug.todo "handle Captain and Seagull"
82 |
83 | The Gren compiler recognizes each `Debug.todo` so if you run into it, you get
84 | an **uncatchable runtime exception** that includes the module name and line
85 | number.
86 |
87 | **Note:** This is not available with `gren make --optimize` or packages. The
88 | idea is that a `todo` can be useful during development, but uncatchable runtime
89 | exceptions should not appear in the resulting applications.
90 |
91 | **Note:** For the equivalent of try/catch error handling in Gren, use modules
92 | like [`Maybe`](#Maybe) and [`Result`](#Result) which guarantee that no error
93 | goes unhandled!
94 |
95 | -}
96 | todo : String -> a
97 | todo =
98 | Gren.Kernel.Debug.todo
99 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Array.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Gren.Kernel.Utils exposing (cmp)
4 | import Basics exposing (EQ, LT)
5 | import Maybe exposing (Just, Nothing)
6 |
7 | */
8 |
9 | var _Array_length = function (array) {
10 | return array.length;
11 | };
12 |
13 | var _Array_initialize = F3(function (size, offset, func) {
14 | var result = new Array(size);
15 |
16 | for (var i = 0; i < size; i++) {
17 | result[i] = func(offset + i);
18 | }
19 |
20 | return result;
21 | });
22 |
23 | var _Array_get = F2(function (index, array) {
24 | var value = array.at(index);
25 |
26 | if (typeof value === "undefined") {
27 | return __Maybe_Nothing;
28 | }
29 |
30 | return __Maybe_Just(value);
31 | });
32 |
33 | var _Array_set = F3(function (index, value, array) {
34 | try {
35 | return array.with(index, value);
36 | } catch (e) {
37 | // assuming RangeError
38 | return array;
39 | }
40 | });
41 |
42 | var _Array_splice0 = F3(function (index, toRemove, array) {
43 | return array.toSpliced(index, toRemove);
44 | });
45 |
46 | var _Array_splice1 = F4(function (index, toRemove, toAdd, array) {
47 | return array.toSpliced(index, toRemove, toAdd);
48 | });
49 |
50 | var _Array_spliceN = F4(function (index, toRemove, toAdd, array) {
51 | return array.toSpliced(index, toRemove, ...toAdd);
52 | });
53 |
54 | var _Array_foldl = F3(function (func, acc, array) {
55 | for (var i = 0; i < array.length; i++) {
56 | acc = A2(func, array[i], acc);
57 | }
58 |
59 | return acc;
60 | });
61 |
62 | var _Array_foldr = F3(function (func, acc, array) {
63 | for (var i = array.length - 1; i >= 0; i--) {
64 | acc = A2(func, array[i], acc);
65 | }
66 |
67 | return acc;
68 | });
69 |
70 | var _Array_indexedFoldl = F3(function (func, acc, array) {
71 | for (var i = 0; i < array.length; i++) {
72 | acc = A3(func, i, array[i], acc);
73 | }
74 |
75 | return acc;
76 | });
77 |
78 | var _Array_indexedFoldr = F3(function (func, acc, array) {
79 | for (var i = array.length - 1; i >= 0; i--) {
80 | acc = A3(func, i, array[i], acc);
81 | }
82 |
83 | return acc;
84 | });
85 |
86 | var _Array_map = F2(function (func, array) {
87 | return array.map(func);
88 | });
89 |
90 | var _Array_indexedMap = F2(function (func, array) {
91 | return array.map(function (value, index) {
92 | return A2(func, index, value);
93 | });
94 | });
95 |
96 | var _Array_filter = F2(function (func, array) {
97 | return array.filter(func);
98 | });
99 |
100 | var _Array_indexedFilter = F2(function (func, array) {
101 | return array.filter(function (value, index) {
102 | return A2(func, index, value);
103 | });
104 | });
105 |
106 | var _Array_flat = function (array) {
107 | return array.flat();
108 | };
109 |
110 | var _Array_flatMap = F2(function (func, array) {
111 | return array.flatMap(func);
112 | });
113 |
114 | var _Array_slice = F3(function (from, to, array) {
115 | return array.slice(from, to);
116 | });
117 |
118 | var _Array_append = F2(function (left, right) {
119 | return left.concat(right);
120 | });
121 |
122 | var _Array_reverse = function (array) {
123 | return array.toReversed();
124 | };
125 |
126 | var _Array_findFirst = F2(function (pred, array) {
127 | for (var i = 0; i < array.length; i++) {
128 | var element = array[i];
129 |
130 | if (pred(element)) {
131 | return __Maybe_Just({ __$index: i, __$value: element });
132 | }
133 | }
134 |
135 | return __Maybe_Nothing;
136 | });
137 |
138 | var _Array_findLast = F2(function (pred, array) {
139 | for (var i = array.length - 1; i >= 0; i--) {
140 | var element = array[i];
141 |
142 | if (pred(element)) {
143 | return __Maybe_Just({ __$index: i, __$value: element });
144 | }
145 | }
146 |
147 | return __Maybe_Nothing;
148 | });
149 |
150 | var _Array_map2 = F3(function (fn, as, bs) {
151 | var result = [];
152 | var lowestLength = as.length < bs.length ? as.length : bs.length;
153 |
154 | for (var i = 0; i < lowestLength; i++) {
155 | result.push(A2(fn, as[i], bs[i]));
156 | }
157 |
158 | return result;
159 | });
160 |
161 | var _Array_map3 = F4(function (fn, as, bs, cs) {
162 | var result = [];
163 | var lowestLength = [as.length, bs.length, cs.length].sort()[0];
164 |
165 | for (var i = 0; i < lowestLength; i++) {
166 | result.push(A3(fn, as[i], bs[i], cs[i]));
167 | }
168 |
169 | return result;
170 | });
171 |
172 | var _Array_sort = function (array) {
173 | return array.toSorted(function (a, b) {
174 | return __Utils_cmp(a, b);
175 | });
176 | };
177 |
178 | var _Array_sortBy = F2(function (fn, array) {
179 | return array.toSorted(function (a, b) {
180 | return __Utils_cmp(fn(a), fn(b));
181 | });
182 | });
183 |
184 | var _Array_sortWith = F2(function (fn, array) {
185 | return array.toSorted(function (a, b) {
186 | var ord = A2(fn, a, b);
187 | return ord === __Basics_EQ ? 0 : ord === __Basics_LT ? -1 : 1;
188 | });
189 | });
190 |
191 | class _Array_Builder {
192 | constructor(target, finalized, array) {
193 | this.__$target = target;
194 | this.__$finalized = finalized;
195 | this.__$array = array;
196 | }
197 | }
198 |
199 | var _Array_emptyBuilder = function (capacity) {
200 | return new _Array_Builder(0, false, new Array(capacity));
201 | };
202 |
203 | var _Array_pushToBuilder = F2(function (value, builder) {
204 | var array = builder.__$array;
205 | var target = builder.__$target;
206 |
207 | if (builder.__$finalized) {
208 | array = array.slice(0, target);
209 | } else {
210 | builder.__$finalized = true;
211 | }
212 |
213 | if (target < array.length) {
214 | array[target] = value;
215 | } else {
216 | array.push(value);
217 | }
218 |
219 | return new _Array_Builder(target + 1, false, array);
220 | });
221 |
222 | var _Array_appendToBuilder = F2(function (array, builder) {
223 | var newArray = _Array_fromBuilder(builder);
224 |
225 | for (var i = 0; i < array.length; i++) {
226 | newArray.push(array[i]);
227 | }
228 |
229 | return new _Array_Builder(newArray.length, false, newArray);
230 | });
231 |
232 | var _Array_toBuilder = function (array) {
233 | return new _Array_Builder(array.length, true, array);
234 | };
235 |
236 | var _Array_fromBuilder = function (builder) {
237 | var result = builder.__$array;
238 |
239 | if (builder.__$finalized) {
240 | result = result.slice(0, builder.__$target);
241 | } else {
242 | builder.__$finalized = true;
243 | result.length = builder.__$target;
244 | }
245 |
246 | return result;
247 | };
248 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Basics.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | */
4 |
5 | // MATH
6 |
7 | var _Basics_add = F2(function (a, b) {
8 | return a + b;
9 | });
10 | var _Basics_sub = F2(function (a, b) {
11 | return a - b;
12 | });
13 | var _Basics_mul = F2(function (a, b) {
14 | return a * b;
15 | });
16 | var _Basics_fdiv = F2(function (a, b) {
17 | return a / b;
18 | });
19 | var _Basics_idiv = F2(function (a, b) {
20 | return Math.trunc(a / b);
21 | });
22 | var _Basics_pow = F2(Math.pow);
23 |
24 | // MORE MATH
25 |
26 | function _Basics_toFloat(x) {
27 | return x;
28 | }
29 | function _Basics_isInfinite(n) {
30 | return n === Infinity || n === -Infinity;
31 | }
32 |
33 | var _Basics_isNaN = isNaN;
34 |
35 | // BOOLEANS
36 |
37 | function _Basics_not(bool) {
38 | return !bool;
39 | }
40 | var _Basics_and = F2(function (a, b) {
41 | return a && b;
42 | });
43 | var _Basics_or = F2(function (a, b) {
44 | return a || b;
45 | });
46 | var _Basics_xor = F2(function (a, b) {
47 | return a !== b;
48 | });
49 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Bitwise.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | */
4 |
5 | var _Bitwise_and = F2(function (a, b) {
6 | return a & b;
7 | });
8 |
9 | var _Bitwise_or = F2(function (a, b) {
10 | return a | b;
11 | });
12 |
13 | var _Bitwise_xor = F2(function (a, b) {
14 | return a ^ b;
15 | });
16 |
17 | function _Bitwise_complement(a) {
18 | return ~a;
19 | }
20 |
21 | var _Bitwise_countLeadingZeros = Math.clz32;
22 |
23 | var _Bitwise_shiftLeftBy = F2(function (offset, a) {
24 | return a << offset;
25 | });
26 |
27 | var _Bitwise_shiftRightBy = F2(function (offset, a) {
28 | return a >> offset;
29 | });
30 |
31 | var _Bitwise_shiftRightZfBy = F2(function (offset, a) {
32 | return a >>> offset;
33 | });
34 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Bytes.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Bytes.Encode as Encode exposing (getLength, write)
4 | import Gren.Kernel.Scheduler exposing (binding, succeed)
5 | import Gren.Kernel.Utils exposing (chr)
6 | import Maybe exposing (Just, Nothing)
7 |
8 | */
9 |
10 | // BYTES
11 |
12 | var _Bytes_empty = new DataView(new ArrayBuffer(0));
13 |
14 | function _Bytes_length(bytes) {
15 | return bytes.byteLength;
16 | }
17 |
18 | var _Bytes_getHostEndianness = F2(function (le, be) {
19 | return __Scheduler_binding(function (callback) {
20 | callback(
21 | __Scheduler_succeed(
22 | new Uint8Array(new Uint32Array([1]))[0] === 1 ? le : be,
23 | ),
24 | );
25 | });
26 | });
27 |
28 | function _Bytes_fromString(str) {
29 | var encoder = new TextEncoder();
30 | var uint8s = encoder.encode(str);
31 | return new DataView(uint8s.buffer);
32 | }
33 |
34 | function _Bytes_toString(bytes) {
35 | var decoder = new TextDecoder("utf-8", { fatal: true });
36 |
37 | try {
38 | return __Maybe_Just(decoder.decode(bytes));
39 | } catch (e) {
40 | return __Maybe_Nothing;
41 | }
42 | }
43 |
44 | function _Bytes_flatten(arrayOfBytes) {
45 | var requiredSize = 0;
46 | for (var i = 0; i < arrayOfBytes.length; i++) {
47 | requiredSize += arrayOfBytes[i].byteLength;
48 | }
49 |
50 | var offset = 0;
51 | var result = new Uint8Array(requiredSize);
52 |
53 | for (var i = 0; i < arrayOfBytes.length; i++) {
54 | var currentBytes = new Uint8Array(arrayOfBytes[i].buffer);
55 | var currentByteLength = arrayOfBytes[i].byteLength;
56 |
57 | for (var j = 0; j < currentByteLength; j++) {
58 | result[offset] = currentBytes[j];
59 | offset++;
60 | }
61 | }
62 |
63 | return new DataView(result.buffer);
64 | }
65 |
66 | // ENCODERS
67 |
68 | function _Bytes_encode(encoder) {
69 | var mutableBytes = new DataView(new ArrayBuffer(__Encode_getLength(encoder)));
70 | A3(__Encode_write, encoder, mutableBytes, 0);
71 | return mutableBytes;
72 | }
73 |
74 | // SIGNED INTEGERS
75 |
76 | var _Bytes_write_i8 = F3(function (mb, i, n) {
77 | mb.setInt8(i, n);
78 | return i + 1;
79 | });
80 | var _Bytes_write_i16 = F4(function (mb, i, n, isLE) {
81 | mb.setInt16(i, n, isLE);
82 | return i + 2;
83 | });
84 | var _Bytes_write_i32 = F4(function (mb, i, n, isLE) {
85 | mb.setInt32(i, n, isLE);
86 | return i + 4;
87 | });
88 |
89 | // UNSIGNED INTEGERS
90 |
91 | var _Bytes_write_u8 = F3(function (mb, i, n) {
92 | mb.setUint8(i, n);
93 | return i + 1;
94 | });
95 | var _Bytes_write_u16 = F4(function (mb, i, n, isLE) {
96 | mb.setUint16(i, n, isLE);
97 | return i + 2;
98 | });
99 | var _Bytes_write_u32 = F4(function (mb, i, n, isLE) {
100 | mb.setUint32(i, n, isLE);
101 | return i + 4;
102 | });
103 |
104 | // FLOATS
105 |
106 | var _Bytes_write_f32 = F4(function (mb, i, n, isLE) {
107 | mb.setFloat32(i, n, isLE);
108 | return i + 4;
109 | });
110 | var _Bytes_write_f64 = F4(function (mb, i, n, isLE) {
111 | mb.setFloat64(i, n, isLE);
112 | return i + 8;
113 | });
114 |
115 | // BYTES
116 |
117 | var _Bytes_write_bytes = F3(function (mb, offset, bytes) {
118 | for (var i = 0, len = bytes.byteLength, limit = len - 4; i <= limit; i += 4) {
119 | mb.setUint32(offset + i, bytes.getUint32(i));
120 | }
121 | for (; i < len; i++) {
122 | mb.setUint8(offset + i, bytes.getUint8(i));
123 | }
124 | return offset + len;
125 | });
126 |
127 | // DECODER
128 |
129 | var _Bytes_decode = F2(function (decoder, bytes) {
130 | try {
131 | return __Maybe_Just(A2(decoder, bytes, 0).__$value);
132 | } catch (e) {
133 | if (e instanceof RangeError) {
134 | return __Maybe_Nothing;
135 | } else {
136 | throw e;
137 | }
138 | }
139 | });
140 |
141 | var _Bytes_read_i8 = F2(function (bytes, offset) {
142 | return { __$offset: offset + 1, __$value: bytes.getInt8(offset) };
143 | });
144 | var _Bytes_read_i16 = F3(function (isLE, bytes, offset) {
145 | return { __$offset: offset + 2, __$value: bytes.getInt16(offset, isLE) };
146 | });
147 | var _Bytes_read_i32 = F3(function (isLE, bytes, offset) {
148 | return { __$offset: offset + 4, __$value: bytes.getInt32(offset, isLE) };
149 | });
150 | var _Bytes_read_u8 = F2(function (bytes, offset) {
151 | return { __$offset: offset + 1, __$value: bytes.getUint8(offset) };
152 | });
153 | var _Bytes_read_u16 = F3(function (isLE, bytes, offset) {
154 | return { __$offset: offset + 2, __$value: bytes.getUint16(offset, isLE) };
155 | });
156 | var _Bytes_read_u32 = F3(function (isLE, bytes, offset) {
157 | return { __$offset: offset + 4, __$value: bytes.getUint32(offset, isLE) };
158 | });
159 | var _Bytes_read_f32 = F3(function (isLE, bytes, offset) {
160 | return { __$offset: offset + 4, __$value: bytes.getFloat32(offset, isLE) };
161 | });
162 | var _Bytes_read_f64 = F3(function (isLE, bytes, offset) {
163 | return { __$offset: offset + 8, __$value: bytes.getFloat64(offset, isLE) };
164 | });
165 |
166 | var _Bytes_read_bytes = F3(function (len, bytes, offset) {
167 | return {
168 | __$offset: offset + len,
169 | __$value: new DataView(bytes.buffer, bytes.byteOffset + offset, len),
170 | };
171 | });
172 |
173 | var _Bytes_decodeFailure = F2(function () {
174 | throw 0;
175 | });
176 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Char.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Gren.Kernel.Utils exposing (chr)
4 |
5 | */
6 |
7 | function _Char_toCode(char) {
8 | return char.codePointAt(0);
9 | }
10 |
11 | function _Char_fromCode(code) {
12 | return __Utils_chr(String.fromCodePoint(code));
13 | }
14 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Debug.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Dict exposing (foldl)
4 | import Set exposing (toArray)
5 |
6 | */
7 |
8 | // LOG
9 |
10 | var _Debug_log__PROD = F2(function (tag, value) {
11 | return value;
12 | });
13 |
14 | var _Debug_log__DEBUG = F2(function (tag, value) {
15 | console.log(tag + ": " + _Debug_toString(value));
16 | return value;
17 | });
18 |
19 | // TODOS
20 |
21 | function _Debug_todo(moduleName, region) {
22 | return function (message) {
23 | _Debug_crash(8, moduleName, region, message);
24 | };
25 | }
26 |
27 | function _Debug_todoCase(moduleName, region, value) {
28 | return function (message) {
29 | _Debug_crash(9, moduleName, region, value, message);
30 | };
31 | }
32 |
33 | // TO STRING
34 |
35 | function _Debug_toString__PROD(value) {
36 | return "";
37 | }
38 |
39 | function _Debug_toString__DEBUG(value) {
40 | return _Debug_toAnsiString(false, value);
41 | }
42 |
43 | function _Debug_toAnsiString(ansi, value) {
44 | if (value == null) {
45 | return _Debug_internalColor(ansi, "");
46 | }
47 |
48 | if (typeof value === "function") {
49 | return _Debug_internalColor(ansi, "");
50 | }
51 |
52 | if (typeof value === "boolean") {
53 | return _Debug_ctorColor(ansi, value ? "True" : "False");
54 | }
55 |
56 | if (typeof value === "number") {
57 | return _Debug_numberColor(ansi, value + "");
58 | }
59 |
60 | if (value instanceof String) {
61 | return _Debug_charColor(ansi, "'" + _Debug_addSlashes(value, true) + "'");
62 | }
63 |
64 | if (typeof value === "string") {
65 | return _Debug_stringColor(
66 | ansi,
67 | '"' + _Debug_addSlashes(value, false) + '"',
68 | );
69 | }
70 |
71 | if (Array.isArray(value)) {
72 | var output = "[";
73 |
74 | value.length > 0 && (output += _Debug_toAnsiString(ansi, value[0]));
75 |
76 | for (var idx = 1; idx < value.length; idx++) {
77 | output += ", " + _Debug_toAnsiString(ansi, value[idx]);
78 | }
79 |
80 | return output + "]";
81 | }
82 |
83 | if (typeof value === "object" && "$" in value) {
84 | var tag = value.$;
85 |
86 | if (typeof tag === "number") {
87 | return _Debug_internalColor(ansi, "");
88 | }
89 |
90 | if (tag === "Set_gren_builtin") {
91 | return (
92 | _Debug_ctorColor(ansi, "Set") +
93 | _Debug_fadeColor(ansi, ".fromArray") +
94 | " " +
95 | _Debug_toAnsiString(ansi, __Set_toArray(value))
96 | );
97 | }
98 |
99 | if (tag === "RBNode_gren_builtin" || tag === "RBEmpty_gren_builtin") {
100 | return (
101 | _Debug_ctorColor(ansi, "Dict") +
102 | _Debug_fadeColor(ansi, ".fromArray") +
103 | " " +
104 | _Debug_toAnsiString(
105 | ansi,
106 | A3(
107 | __Dict_foldl,
108 | F3(function (key, value, acc) {
109 | acc.push({ key: key, value: value });
110 | return acc;
111 | }),
112 | [],
113 | value,
114 | ),
115 | )
116 | );
117 | }
118 |
119 | var output = "";
120 | for (var i in value) {
121 | if (i === "$") continue;
122 | var str = _Debug_toAnsiString(ansi, value[i]);
123 | var c0 = str[0];
124 | var parenless =
125 | c0 === "{" ||
126 | c0 === "(" ||
127 | c0 === "[" ||
128 | c0 === "<" ||
129 | c0 === '"' ||
130 | str.indexOf(" ") < 0;
131 | output += " " + (parenless ? str : "(" + str + ")");
132 | }
133 | return _Debug_ctorColor(ansi, tag) + output;
134 | }
135 |
136 | if (value instanceof DataView) {
137 | return _Debug_stringColor(ansi, "<" + value.byteLength + " bytes>");
138 | }
139 |
140 | if (typeof File !== "undefined" && value instanceof File) {
141 | return _Debug_internalColor(ansi, "<" + value.name + ">");
142 | }
143 |
144 | if (
145 | typeof _Array_Builder !== "undefined" &&
146 | value instanceof _Array_Builder
147 | ) {
148 | return _Debug_toAnsiString(ansi, value.__$array.slice(0, value.__$target));
149 | }
150 |
151 | if (typeof value === "object") {
152 | var output = [];
153 | for (var key in value) {
154 | var field = key[0] === "_" ? key.slice(1) : key;
155 | output.push(
156 | _Debug_fadeColor(ansi, field) +
157 | " = " +
158 | _Debug_toAnsiString(ansi, value[key]),
159 | );
160 | }
161 | if (output.length === 0) {
162 | return "{}";
163 | }
164 | return "{ " + output.join(", ") + " }";
165 | }
166 |
167 | return _Debug_internalColor(ansi, "");
168 | }
169 |
170 | function _Debug_addSlashes(str, isChar) {
171 | var s = str
172 | .replace(/\\/g, "\\\\")
173 | .replace(/\n/g, "\\n")
174 | .replace(/\t/g, "\\t")
175 | .replace(/\r/g, "\\r")
176 | .replace(/\v/g, "\\v")
177 | .replace(/\0/g, "\\0");
178 |
179 | if (isChar) {
180 | return s.replace(/\'/g, "\\'");
181 | } else {
182 | return s.replace(/\"/g, '\\"');
183 | }
184 | }
185 |
186 | function _Debug_ctorColor(ansi, string) {
187 | return ansi ? "\x1b[96m" + string + "\x1b[0m" : string;
188 | }
189 |
190 | function _Debug_numberColor(ansi, string) {
191 | return ansi ? "\x1b[95m" + string + "\x1b[0m" : string;
192 | }
193 |
194 | function _Debug_stringColor(ansi, string) {
195 | return ansi ? "\x1b[93m" + string + "\x1b[0m" : string;
196 | }
197 |
198 | function _Debug_charColor(ansi, string) {
199 | return ansi ? "\x1b[92m" + string + "\x1b[0m" : string;
200 | }
201 |
202 | function _Debug_fadeColor(ansi, string) {
203 | return ansi ? "\x1b[37m" + string + "\x1b[0m" : string;
204 | }
205 |
206 | function _Debug_internalColor(ansi, string) {
207 | return ansi ? "\x1b[36m" + string + "\x1b[0m" : string;
208 | }
209 |
210 | function _Debug_toHexDigit(n) {
211 | return String.fromCharCode(n < 10 ? 48 + n : 55 + n);
212 | }
213 |
214 | // CRASH
215 |
216 | function _Debug_crash__PROD(identifier) {
217 | throw new Error(
218 | "https://github.com/gren-lang/core/blob/1.0.0/hints/" + identifier + ".md",
219 | );
220 | }
221 |
222 | function _Debug_crash__DEBUG(identifier, fact1, fact2, fact3, fact4) {
223 | switch (identifier) {
224 | case 0:
225 | throw new Error(
226 | 'What node should I take over? In JavaScript I need something like:\n\n Gren.Main.init({\n node: document.getElementById("gren-node")\n })\n\nYou need to do this with any Browser.sandbox or Browser.element program.',
227 | );
228 |
229 | case 1:
230 | throw new Error(
231 | "Browser.application programs cannot handle URLs like this:\n\n " +
232 | document.location.href +
233 | "\n\nWhat is the root? The root of your file system?",
234 | );
235 |
236 | case 2:
237 | var jsonErrorString = fact1;
238 | throw new Error(
239 | "Problem with the flags given to your Gren program on initialization.\n\n" +
240 | jsonErrorString,
241 | );
242 |
243 | case 3:
244 | var portName = fact1;
245 | throw new Error(
246 | "There can only be one port named `" +
247 | portName +
248 | "`, but your program has multiple.",
249 | );
250 |
251 | case 4:
252 | var portName = fact1;
253 | var problem = fact2;
254 | throw new Error(
255 | "Trying to send an unexpected type of value through port `" +
256 | portName +
257 | "`:\n" +
258 | problem,
259 | );
260 |
261 | case 5:
262 | throw new Error(
263 | 'Trying to use `(==)` on functions.\nThere is no way to know if functions are "the same" in the Gren sense.\nRead more about this at https://package.gren-lang.org/packages/gren-lang/core/latest/Basics#== which describes why it is this way and what the better version will look like.',
264 | );
265 |
266 | case 6:
267 | var moduleName = fact1;
268 | throw new Error(
269 | "Your page is loading multiple Gren scripts with a module named " +
270 | moduleName +
271 | ". Maybe a duplicate script is getting loaded accidentally? If not, rename one of them so I know which is which!",
272 | );
273 |
274 | case 8:
275 | var moduleName = fact1;
276 | var region = fact2;
277 | var message = fact3;
278 | throw new Error(
279 | "TODO in module `" +
280 | moduleName +
281 | "` " +
282 | _Debug_regionToString(region) +
283 | "\n\n" +
284 | message,
285 | );
286 |
287 | case 9:
288 | var moduleName = fact1;
289 | var region = fact2;
290 | var value = fact3;
291 | var message = fact4;
292 | throw new Error(
293 | "TODO in module `" +
294 | moduleName +
295 | "` from the `case` expression " +
296 | _Debug_regionToString(region) +
297 | "\n\nIt received the following value:\n\n " +
298 | _Debug_toString(value).replace("\n", "\n ") +
299 | "\n\nBut the branch that handles it says:\n\n " +
300 | message.replace("\n", "\n "),
301 | );
302 |
303 | case 10:
304 | throw new Error("Bug in https://github.com/gren-lang/core/issues");
305 |
306 | case 11:
307 | throw new Error("Cannot perform mod 0. Division by zero error.");
308 | }
309 | }
310 |
311 | function _Debug_regionToString(region) {
312 | if (region.__$start.__$line === region.__$end.__$line) {
313 | return "on line " + region.__$start.__$line;
314 | }
315 | return (
316 | "on lines " + region.__$start.__$line + " through " + region.__$end.__$line
317 | );
318 | }
319 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Math.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Gren.Kernel.Debug exposing (crash)
4 |
5 | */
6 |
7 | // MATH
8 |
9 | var _Math_remainderBy = F2(function (b, a) {
10 | return a % b;
11 | });
12 |
13 | // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
14 | var _Math_modBy = F2(function (modulus, x) {
15 | var answer = x % modulus;
16 | return modulus === 0
17 | ? __Debug_crash(11)
18 | : (answer > 0 && modulus < 0) || (answer < 0 && modulus > 0)
19 | ? answer + modulus
20 | : answer;
21 | });
22 |
23 | // CONSTANTS
24 |
25 | var _Math_pi = Math.PI;
26 | var _Math_e = Math.E;
27 | var _Math_maxSafeInteger = Number.MAX_SAFE_INTEGER;
28 | var _Math_minSafeInteger = Number.MIN_SAFE_INTEGER;
29 | var _Math_maxFloat = Number.MAX_VALUE;
30 |
31 | // TRIGONOMETRY
32 |
33 | var _Math_cos = Math.cos;
34 | var _Math_sin = Math.sin;
35 | var _Math_tan = Math.tan;
36 | var _Math_acos = Math.acos;
37 | var _Math_asin = Math.asin;
38 | var _Math_atan = Math.atan;
39 | var _Math_atan2 = F2(Math.atan2);
40 |
41 | // MORE MATH
42 |
43 | var _Math_truncate = Math.trunc;
44 | var _Math_ceiling = Math.ceil;
45 | var _Math_floor = Math.floor;
46 | var _Math_round = Math.round;
47 | var _Math_sqrt = Math.sqrt;
48 | var _Math_log = Math.log;
49 | var _Math_log10 = Math.log10;
50 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Process.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Gren.Kernel.Scheduler exposing (binding, succeed)
4 |
5 | */
6 |
7 | function _Process_sleep(time) {
8 | return __Scheduler_binding(function (callback) {
9 | var id = setTimeout(function () {
10 | callback(__Scheduler_succeed({}));
11 | }, time);
12 |
13 | return function () {
14 | clearTimeout(id);
15 | };
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Regex.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Maybe exposing (Just, Nothing)
4 |
5 | */
6 |
7 | // CREATE
8 |
9 | var _Regex_never = /.^/;
10 |
11 | var _Regex_fromStringWith = F2(function (options, string) {
12 | if (string.trim().length === 0) {
13 | return __Maybe_Nothing;
14 | }
15 |
16 | var flags = "g";
17 | if (options.__$multiline) {
18 | flags += "m";
19 | }
20 | if (options.__$caseInsensitive) {
21 | flags += "i";
22 | }
23 |
24 | try {
25 | return __Maybe_Just(new RegExp(string, flags));
26 | } catch (error) {
27 | return __Maybe_Nothing;
28 | }
29 | });
30 |
31 | // USE
32 |
33 | var _Regex_contains = F2(function (re, string) {
34 | return string.match(re) !== null;
35 | });
36 |
37 | var _Regex_findAtMost = F3(function (n, re, str) {
38 | var out = [];
39 | var number = 0;
40 | var string = str;
41 | var lastIndex = re.lastIndex;
42 | var prevLastIndex = -1;
43 | var result;
44 | while (number++ < n && (result = re.exec(string))) {
45 | if (prevLastIndex == re.lastIndex) break;
46 | var i = result.length - 1;
47 | var subs = new Array(i);
48 | while (i > 0) {
49 | var submatch = result[i];
50 | subs[--i] = submatch ? __Maybe_Just(submatch) : __Maybe_Nothing;
51 | }
52 | out.push({
53 | __$match: result[0],
54 | __$index: result.index,
55 | __$number: number,
56 | __$submatches: subs,
57 | });
58 | prevLastIndex = re.lastIndex;
59 | }
60 | re.lastIndex = lastIndex;
61 | return out;
62 | });
63 |
64 | var _Regex_replaceAtMost = F4(function (n, re, replacer, string) {
65 | var count = 0;
66 | function jsReplacer(match) {
67 | if (count++ >= n) {
68 | return match;
69 | }
70 | var i = arguments.length - 3;
71 | var submatches = new Array(i);
72 | while (i > 0) {
73 | var submatch = arguments[i];
74 | submatches[--i] = submatch ? __Maybe_Just(submatch) : __Maybe_Nothing;
75 | }
76 | return replacer({
77 | __$match: match,
78 | __$index: arguments[arguments.length - 2],
79 | __$number: count,
80 | __$submatches: submatches,
81 | });
82 | }
83 | return string.replace(re, jsReplacer);
84 | });
85 |
86 | var _Regex_splitAtMost = F3(function (n, re, str) {
87 | return str.split(re, n);
88 | });
89 |
90 | var _Regex_infinity = Number.MAX_SAFE_INTEGER;
91 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Scheduler.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | */
4 |
5 | // TASKS
6 |
7 | function _Scheduler_succeed(value) {
8 | return {
9 | $: __1_SUCCEED,
10 | __value: value,
11 | };
12 | }
13 |
14 | function _Scheduler_fail(error) {
15 | return {
16 | $: __1_FAIL,
17 | __value: error,
18 | };
19 | }
20 |
21 | function _Scheduler_binding(callback) {
22 | return {
23 | $: __1_BINDING,
24 | __callback: callback,
25 | __kill: null,
26 | };
27 | }
28 |
29 | var _Scheduler_andThen = F2(function (callback, task) {
30 | return {
31 | $: __1_AND_THEN,
32 | __callback: callback,
33 | __task: task,
34 | };
35 | });
36 |
37 | var _Scheduler_onError = F2(function (callback, task) {
38 | return {
39 | $: __1_ON_ERROR,
40 | __callback: callback,
41 | __task: task,
42 | };
43 | });
44 |
45 | function _Scheduler_receive(callback) {
46 | return {
47 | $: __1_RECEIVE,
48 | __callback: callback,
49 | };
50 | }
51 |
52 | function _Scheduler_concurrent(tasks) {
53 | if (tasks.length === 0) return _Scheduler_succeed([]);
54 |
55 | return _Scheduler_binding(function (callback) {
56 | let count = 0;
57 | let results = new Array(tasks.length);
58 | let procs;
59 |
60 | function killAll() {
61 | procs.forEach(_Scheduler_rawKill);
62 | }
63 |
64 | function onError(e) {
65 | killAll();
66 | callback(_Scheduler_fail(e));
67 | }
68 |
69 | procs = tasks.map((task, i) => {
70 | function onSuccess(res) {
71 | results[i] = res;
72 | count++;
73 | if (count === tasks.length) {
74 | callback(_Scheduler_succeed(results));
75 | }
76 | }
77 | const success = A2(_Scheduler_andThen, onSuccess, task);
78 | const handled = A2(_Scheduler_onError, onError, success);
79 | return _Scheduler_rawSpawn(handled);
80 | });
81 |
82 | return killAll;
83 | });
84 | }
85 |
86 | var _Scheduler_map2 = F3(function (callback, taskA, taskB) {
87 | function combine([resA, resB]) {
88 | return _Scheduler_succeed(A2(callback, resA, resB));
89 | }
90 | return A2(_Scheduler_andThen, combine, _Scheduler_concurrent([taskA, taskB]));
91 | });
92 |
93 | // PROCESSES
94 |
95 | var _Scheduler_guid = 0;
96 |
97 | function _Scheduler_rawSpawn(task) {
98 | var proc = {
99 | $: __2_PROCESS,
100 | __id: _Scheduler_guid++,
101 | __root: task,
102 | __stack: null,
103 | __mailbox: [],
104 | };
105 |
106 | _Scheduler_enqueue(proc);
107 |
108 | return proc;
109 | }
110 |
111 | function _Scheduler_spawn(task) {
112 | return _Scheduler_binding(function (callback) {
113 | callback(_Scheduler_succeed(_Scheduler_rawSpawn(task)));
114 | });
115 | }
116 |
117 | function _Scheduler_rawSend(proc, msg) {
118 | proc.__mailbox.push(msg);
119 | _Scheduler_enqueue(proc);
120 | }
121 |
122 | var _Scheduler_send = F2(function (proc, msg) {
123 | return _Scheduler_binding(function (callback) {
124 | _Scheduler_rawSend(proc, msg);
125 | callback(_Scheduler_succeed({}));
126 | });
127 | });
128 |
129 | function _Scheduler_kill(proc) {
130 | return _Scheduler_binding(function (callback) {
131 | _Scheduler_rawKill(proc);
132 |
133 | callback(_Scheduler_succeed({}));
134 | });
135 | }
136 |
137 | function _Scheduler_rawKill(proc) {
138 | var task = proc.__root;
139 | if (task && task.$ === __1_BINDING && task.__kill) {
140 | task.__kill();
141 | }
142 |
143 | proc.__root = null;
144 | }
145 |
146 | /* STEP PROCESSES
147 |
148 | type alias Process =
149 | { $ : tag
150 | , id : unique_id
151 | , root : Task
152 | , stack : null | { $: SUCCEED | FAIL, a: callback, b: stack }
153 | , mailbox : [msg]
154 | }
155 |
156 | */
157 |
158 | var _Scheduler_working = false;
159 | var _Scheduler_queue = [];
160 |
161 | function _Scheduler_enqueue(proc) {
162 | _Scheduler_queue.push(proc);
163 | if (_Scheduler_working) {
164 | return;
165 | }
166 | _Scheduler_working = true;
167 | // Make sure tasks created during _step are run
168 | while (_Scheduler_queue.length > 0) {
169 | const activeProcs = _Scheduler_queue;
170 | _Scheduler_queue = [];
171 |
172 | for (const proc of activeProcs) {
173 | _Scheduler_step(proc);
174 | }
175 | }
176 | _Scheduler_working = false;
177 | }
178 |
179 | function _Scheduler_step(proc) {
180 | while (proc.__root) {
181 | var rootTag = proc.__root.$;
182 | if (rootTag === __1_SUCCEED || rootTag === __1_FAIL) {
183 | while (proc.__stack && proc.__stack.$ !== rootTag) {
184 | proc.__stack = proc.__stack.__rest;
185 | }
186 | if (!proc.__stack) {
187 | return;
188 | }
189 | proc.__root = proc.__stack.__callback(proc.__root.__value);
190 | proc.__stack = proc.__stack.__rest;
191 | } else if (rootTag === __1_BINDING) {
192 | proc.__root.__kill = proc.__root.__callback(function (newRoot) {
193 | proc.__root = newRoot;
194 | _Scheduler_enqueue(proc);
195 | });
196 | return;
197 | } else if (rootTag === __1_RECEIVE) {
198 | if (proc.__mailbox.length === 0) {
199 | return;
200 | }
201 | proc.__root = proc.__root.__callback(proc.__mailbox.shift());
202 | } // if (rootTag === __1_AND_THEN || rootTag === __1_ON_ERROR)
203 | else {
204 | proc.__stack = {
205 | $: rootTag === __1_AND_THEN ? __1_SUCCEED : __1_FAIL,
206 | __callback: proc.__root.__callback,
207 | __rest: proc.__stack,
208 | };
209 | proc.__root = proc.__root.__task;
210 | }
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Stream.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Stream exposing (Locked, Closed, Cancelled)
4 | import Gren.Kernel.Scheduler exposing (binding, succeed, fail, rawSpawn)
5 |
6 | */
7 |
8 | var _Stream_read = function (stream) {
9 | return __Scheduler_binding(function (callback) {
10 | if (stream.locked) {
11 | return callback(__Scheduler_fail(__Stream_Locked));
12 | }
13 |
14 | const reader = stream.getReader();
15 | reader
16 | .read()
17 | .then(({ done, value }) => {
18 | reader.releaseLock();
19 |
20 | if (done) {
21 | return callback(__Scheduler_fail(__Stream_Closed));
22 | }
23 |
24 | if (value instanceof Uint8Array) {
25 | value = new DataView(
26 | value.buffer,
27 | value.byteOffset,
28 | value.byteLength,
29 | );
30 | }
31 |
32 | callback(__Scheduler_succeed(value));
33 | })
34 | .catch((err) => {
35 | reader.releaseLock();
36 | callback(
37 | __Scheduler_fail(
38 | __Stream_Cancelled(_Stream_cancellationErrorString(err)),
39 | ),
40 | );
41 | });
42 | });
43 | };
44 |
45 | var _Stream_cancellationErrorString = function (err) {
46 | if (err instanceof Error) {
47 | return err.toString();
48 | }
49 |
50 | if (typeof err === "string") {
51 | return err;
52 | }
53 |
54 | return "Unknown error";
55 | };
56 |
57 | var _Stream_write = F2(function (value, stream) {
58 | return __Scheduler_binding(function (callback) {
59 | if (stream.locked) {
60 | return callback(__Scheduler_fail(__Stream_Locked));
61 | }
62 |
63 | if (value instanceof DataView) {
64 | value = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
65 | }
66 |
67 | const writer = stream.getWriter();
68 | writer.ready
69 | .then(() => {
70 | const writePromise = writer.write(value);
71 | writer.releaseLock();
72 | return writePromise;
73 | })
74 | .then(() => {
75 | callback(__Scheduler_succeed(stream));
76 | })
77 | .catch((err) => {
78 | callback(
79 | __Scheduler_fail(
80 | __Stream_Cancelled(_Stream_cancellationErrorString(err)),
81 | ),
82 | );
83 | });
84 | });
85 | });
86 |
87 | var _Stream_enqueue = F2(function (value, stream) {
88 | return __Scheduler_binding(function (callback) {
89 | if (stream.locked) {
90 | return callback(__Scheduler_fail(__Stream_Locked));
91 | }
92 |
93 | if (value instanceof DataView) {
94 | value = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
95 | }
96 |
97 | const writer = stream.getWriter();
98 | writer.ready.then(() => {
99 | writer.write(value);
100 | writer.releaseLock();
101 |
102 | callback(__Scheduler_succeed(stream));
103 | });
104 | });
105 | });
106 |
107 | var _Stream_cancelReadable = F2(function (reason, stream) {
108 | return __Scheduler_binding(function (callback) {
109 | if (stream.locked) {
110 | return callback(__Scheduler_fail(__Stream_Locked));
111 | }
112 |
113 | stream.cancel(reason).then(() => {
114 | callback(__Scheduler_succeed({}));
115 | });
116 | });
117 | });
118 |
119 | var _Stream_cancelWritable = F2(function (reason, stream) {
120 | return __Scheduler_binding(function (callback) {
121 | if (stream.locked) {
122 | return callback(__Scheduler_fail(__Stream_Locked));
123 | }
124 |
125 | stream.abort(reason).then(() => {
126 | callback(__Scheduler_succeed({}));
127 | });
128 | });
129 | });
130 |
131 | var _Stream_closeWritable = function (stream) {
132 | return __Scheduler_binding(function (callback) {
133 | if (stream.locked) {
134 | return callback(__Scheduler_fail(__Stream_Locked));
135 | }
136 |
137 | const writer = stream.getWriter();
138 | writer.close();
139 | writer.releaseLock();
140 |
141 | callback(__Scheduler_succeed({}));
142 | });
143 | };
144 |
145 | var _Stream_pipeThrough = F2(function (transformer, readable) {
146 | return __Scheduler_binding(function (callback) {
147 | if (readable.locked || transformer.writable.locked) {
148 | return callback(__Scheduler_fail(__Stream_Locked));
149 | }
150 |
151 | const transformedReader = readable.pipeThrough(transformer);
152 | return callback(__Scheduler_succeed(transformedReader));
153 | });
154 | });
155 |
156 | var _Stream_pipeTo = F2(function (writable, readable) {
157 | return __Scheduler_binding(function (callback) {
158 | if (readable.locked || writable.locked) {
159 | return callback(__Scheduler_fail(__Stream_Locked));
160 | }
161 |
162 | readable
163 | .pipeTo(writable)
164 | .then(() => {
165 | callback(__Scheduler_succeed({}));
166 | })
167 | .catch((err) => {
168 | callback(
169 | __Scheduler_fail(
170 | __Stream_Cancelled(_Stream_cancellationErrorString(err)),
171 | ),
172 | );
173 | });
174 | });
175 | });
176 |
177 | var _Stream_identityTransformation = F2(function (readCapacity, writeCapacity) {
178 | return __Scheduler_binding(function (callback) {
179 | const transformStream = new TransformStream(
180 | {},
181 | new CountQueuingStrategy({ highWaterMark: writeCapacity }),
182 | new CountQueuingStrategy({ highWaterMark: readCapacity }),
183 | );
184 |
185 | return callback(__Scheduler_succeed(transformStream));
186 | });
187 | });
188 |
189 | var _Stream_customTransformation = F4(
190 | function (toAction, initState, readCapacity, writeCapacity) {
191 | return __Scheduler_binding(function (callback) {
192 | const transformStream = new TransformStream(
193 | {
194 | start() {
195 | this.state = initState;
196 | },
197 | transform(chunk, controller) {
198 | if (chunk instanceof Uint8Array) {
199 | chunk = new DataView(
200 | chunk.buffer,
201 | chunk.byteOffset,
202 | chunk.byteLength,
203 | );
204 | }
205 |
206 | const action = A2(toAction, this.state, chunk);
207 | switch (action.__$ctor) {
208 | case "UpdateState":
209 | this.state = action.__$state;
210 | break;
211 | case "Send":
212 | this.state = action.__$state;
213 | for (let value of action.__$send) {
214 | if (value instanceof DataView) {
215 | value = new Uint8Array(
216 | value.buffer,
217 | value.byteOffset,
218 | value.byteLength,
219 | );
220 | }
221 |
222 | controller.enqueue(value);
223 | }
224 | break;
225 | case "Close":
226 | for (let value of action.__$send) {
227 | if (value instanceof DataView) {
228 | value = new Uint8Array(
229 | value.buffer,
230 | value.byteOffset,
231 | value.byteLength,
232 | );
233 | }
234 |
235 | controller.enqueue(value);
236 | }
237 | controller.terminate();
238 | break;
239 | case "Cancel":
240 | controller.error(action.__$cancelReason);
241 | break;
242 | }
243 | },
244 | },
245 | new CountQueuingStrategy({ highWaterMark: writeCapacity }),
246 | new CountQueuingStrategy({ highWaterMark: readCapacity }),
247 | );
248 |
249 | return callback(__Scheduler_succeed(transformStream));
250 | });
251 | },
252 | );
253 |
254 | var _Stream_readable = function (transformStream) {
255 | return transformStream.readable;
256 | };
257 |
258 | var _Stream_writable = function (transformStream) {
259 | return transformStream.writable;
260 | };
261 |
262 | var _Stream_textEncoder = __Scheduler_binding(function (callback) {
263 | return callback(__Scheduler_succeed(new TextEncoderStream()));
264 | });
265 |
266 | var _Stream_textDecoder = __Scheduler_binding(function (callback) {
267 | return callback(__Scheduler_succeed(new TextDecoderStream()));
268 | });
269 |
270 | var _Stream_compressor = function (algo) {
271 | return __Scheduler_binding(function (callback) {
272 | return callback(__Scheduler_succeed(new CompressionStream(algo)));
273 | });
274 | };
275 |
276 | var _Stream_decompressor = function (algo) {
277 | return __Scheduler_binding(function (callback) {
278 | return callback(__Scheduler_succeed(new DecompressionStream(algo)));
279 | });
280 | };
281 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/String.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Gren.Kernel.Utils exposing (chr)
4 | import Maybe exposing (Just, Nothing)
5 |
6 | */
7 |
8 | var _String_pushFirst = F2(function (char, string) {
9 | return char + string;
10 | });
11 |
12 | var _String_pushLast = F2(function (char, string) {
13 | return string + char;
14 | });
15 |
16 | var _String_popFirst = function (string) {
17 | if (string.length <= 0) {
18 | return __Maybe_Nothing;
19 | }
20 |
21 | var firstPointNumber = string.codePointAt(0);
22 | var firstChar = String.fromCodePoint(firstPointNumber);
23 |
24 | return __Maybe_Just({
25 | __$first: __Utils_chr(firstChar),
26 | __$rest: string.slice(firstChar.length),
27 | });
28 | };
29 |
30 | var _String_popLast = function (string) {
31 | if (string.length <= 0) {
32 | return __Maybe_Nothing;
33 | }
34 |
35 | var possibleLastPointIdx = string.length - 2;
36 | var possibleLastPoint = string.codePointAt(possibleLastPointIdx);
37 |
38 | if (possibleLastPoint === string.charCodeAt(possibleLastPointIdx)) {
39 | // last char is a unit
40 | return __Maybe_Just({
41 | __$last: __Utils_chr(string[string.length - 1]),
42 | __$rest: string.slice(string.length - 1),
43 | });
44 | }
45 |
46 | // last char is a point
47 | return __Maybe_Just({
48 | __$last: __Utils_chr(String.fromCodePoint(possibleLastPoint)),
49 | __$rest: string.slice(string.length - 2),
50 | });
51 | };
52 |
53 | var _String_append = F2(function (a, b) {
54 | return a + b;
55 | });
56 |
57 | var _String_repeat = F2(function (num, chunk) {
58 | try {
59 | return chunk.repeat(num);
60 | } catch (error) {
61 | if (error.name === "RangeError") {
62 | return "";
63 | } else {
64 | throw error;
65 | }
66 | }
67 | });
68 |
69 | var _String_foldl = F3(function (func, state, string) {
70 | for (let char of string) {
71 | state = A2(func, __Utils_chr(char), state);
72 | }
73 |
74 | return state;
75 | });
76 |
77 | var _String_foldr = F3(function (func, state, string) {
78 | let reversed = [];
79 |
80 | for (let char of string) {
81 | reversed.unshift(char);
82 | }
83 |
84 | for (let char of reversed) {
85 | state = A2(func, __Utils_chr(char), state);
86 | }
87 |
88 | return state;
89 | });
90 |
91 | var _String_split = F2(function (sep, str) {
92 | return str.split(sep);
93 | });
94 |
95 | var _String_join = F2(function (sep, strs) {
96 | return strs.join(sep);
97 | });
98 |
99 | var _String_slice = F3(function (start, end, str) {
100 | if (start < 0) {
101 | start = str.length + start;
102 | }
103 |
104 | if (end < 0) {
105 | end = str.length + end;
106 | }
107 |
108 | if (start >= end) {
109 | return "";
110 | }
111 |
112 | let index = 0;
113 | let result = "";
114 |
115 | for (let char of str) {
116 | if (index < start) {
117 | index++;
118 | continue;
119 | }
120 |
121 | if (index >= end) {
122 | break;
123 | }
124 |
125 | result += char;
126 | index++;
127 | }
128 |
129 | return result;
130 | });
131 |
132 | function _String_trim(str) {
133 | return str.trim();
134 | }
135 |
136 | function _String_trimLeft(str) {
137 | return str.replace(/^\s+/, "");
138 | }
139 |
140 | function _String_trimRight(str) {
141 | return str.replace(/\s+$/, "");
142 | }
143 |
144 | function _String_words(str) {
145 | return str.trim().split(/\s+/g);
146 | }
147 |
148 | function _String_lines(str) {
149 | return str.split(/\r\n|\r|\n/g);
150 | }
151 |
152 | function _String_toUpper(str) {
153 | return str.toUpperCase();
154 | }
155 |
156 | function _String_toLower(str) {
157 | return str.toLowerCase();
158 | }
159 |
160 | var _String_any = F2(function (isGood, string) {
161 | for (let char of string) {
162 | if (isGood(__Utils_chr(char))) {
163 | return true;
164 | }
165 | }
166 |
167 | return false;
168 | });
169 |
170 | var _String_contains = F2(function (sub, str) {
171 | return str.indexOf(sub) > -1;
172 | });
173 |
174 | var _String_startsWith = F2(function (sub, str) {
175 | return str.indexOf(sub) === 0;
176 | });
177 |
178 | var _String_endsWith = F2(function (sub, str) {
179 | return (
180 | str.length >= sub.length && str.lastIndexOf(sub) === str.length - sub.length
181 | );
182 | });
183 |
184 | var _String_indexOf = F2(function (sub, str) {
185 | var ret = str.indexOf(sub);
186 |
187 | if (ret > -1) {
188 | return __Maybe_Just(ret);
189 | }
190 |
191 | return __Maybe_Nothing;
192 | });
193 |
194 | var _String_lastIndexOf = F2(function (sub, str) {
195 | var ret = str.lastIndexOf(sub);
196 |
197 | if (ret > -1) {
198 | return __Maybe_Just(ret);
199 | }
200 |
201 | return __Maybe_Nothing;
202 | });
203 |
204 | var _String_indexes = F2(function (sub, str) {
205 | var subLen = sub.length;
206 |
207 | if (subLen < 1) {
208 | return [];
209 | }
210 |
211 | var i = 0;
212 | var is = [];
213 |
214 | while ((i = str.indexOf(sub, i)) > -1) {
215 | is.push(i);
216 | i = i + subLen;
217 | }
218 |
219 | return is;
220 | });
221 |
222 | // TO STRING
223 |
224 | function _String_fromNumber(number) {
225 | return number + "";
226 | }
227 |
228 | // INT CONVERSIONS
229 |
230 | function _String_toInt(str) {
231 | var total = 0;
232 | var code0 = str.charCodeAt(0);
233 | var start = code0 == 0x2b /* + */ || code0 == 0x2d /* - */ ? 1 : 0;
234 |
235 | for (var i = start; i < str.length; ++i) {
236 | var code = str.charCodeAt(i);
237 | if (code < 0x30 || 0x39 < code) {
238 | return __Maybe_Nothing;
239 | }
240 | total = 10 * total + code - 0x30;
241 | }
242 |
243 | return i == start
244 | ? __Maybe_Nothing
245 | : __Maybe_Just(code0 == 0x2d ? -total : total);
246 | }
247 |
248 | // FLOAT CONVERSIONS
249 |
250 | function _String_toFloat(s) {
251 | // check if it is a hex, octal, or binary number
252 | if (s.length === 0 || /[\sxbo]/.test(s)) {
253 | return __Maybe_Nothing;
254 | }
255 | var n = +s;
256 | // faster isNaN check
257 | return n === n ? __Maybe_Just(n) : __Maybe_Nothing;
258 | }
259 |
260 | function _String_fromArray(chars) {
261 | return chars.join("");
262 | }
263 |
264 | // UNITS
265 |
266 | var _String_unitLength = function (str) {
267 | return str.length;
268 | };
269 |
270 | var _String_getUnit = F2(function (index, str) {
271 | var ret = str.at(index);
272 |
273 | if (typeof ret === "undefined") {
274 | return __Maybe_Nothing;
275 | }
276 |
277 | return __Maybe_Just(__Utils_chr(char));
278 | });
279 |
280 | var _String_foldlUnits = F3(function (fn, state, str) {
281 | for (let i = 0; i < str.length; i++) {
282 | state = A2(fn, str[i], state);
283 | }
284 |
285 | return state;
286 | });
287 |
288 | var _String_foldrUnits = F3(function (fn, state, str) {
289 | for (let i = str.length - 1; i < 0; i--) {
290 | state = A2(fn, str[i], state);
291 | }
292 |
293 | return state;
294 | });
295 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Time.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Time exposing (customZone, Name, Offset)
4 | import Gren.Kernel.Scheduler exposing (binding, succeed)
5 |
6 | */
7 |
8 | function _Time_now(millisToPosix) {
9 | return __Scheduler_binding(function (callback) {
10 | callback(__Scheduler_succeed(millisToPosix(Date.now())));
11 | });
12 | }
13 |
14 | var _Time_setInterval = F2(function (interval, task) {
15 | return __Scheduler_binding(function (callback) {
16 | var id = setInterval(function () {
17 | _Scheduler_rawSpawn(task);
18 | }, interval);
19 | return function () {
20 | clearInterval(id);
21 | };
22 | });
23 | });
24 |
25 | function _Time_here() {
26 | return __Scheduler_binding(function (callback) {
27 | callback(
28 | __Scheduler_succeed(
29 | A2(__Time_customZone, -new Date().getTimezoneOffset(), []),
30 | ),
31 | );
32 | });
33 | }
34 |
35 | function _Time_getZoneName() {
36 | return __Scheduler_binding(function (callback) {
37 | try {
38 | var name = __Time_Name(Intl.DateTimeFormat().resolvedOptions().timeZone);
39 | } catch (e) {
40 | var name = __Time_Offset(new Date().getTimezoneOffset());
41 | }
42 | callback(__Scheduler_succeed(name));
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/src/Gren/Kernel/Utils.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Basics exposing (LT, EQ, GT)
4 | import Dict exposing (foldl)
5 | import Gren.Kernel.Debug exposing (crash)
6 | import Set exposing (toArray)
7 | import Gren.Kernel.Array exposing (fromBuilder)
8 |
9 | */
10 |
11 | // EQUALITY
12 |
13 | function _Utils_eq(x, y) {
14 | for (
15 | var pair, stack = [], isEqual = _Utils_eqHelp(x, y, 0, stack);
16 | isEqual && (pair = stack.pop());
17 | isEqual = _Utils_eqHelp(pair.a, pair.b, 0, stack)
18 | ) {}
19 |
20 | return isEqual;
21 | }
22 |
23 | function _Utils_eqHelp(x, y, depth, stack) {
24 | if (x === y) {
25 | return true;
26 | }
27 |
28 | if (typeof x !== "object" || x === null || y === null) {
29 | typeof x === "function" && __Debug_crash(5);
30 | return false;
31 | }
32 |
33 | if (depth > 100) {
34 | stack.push({ a: x, b: y });
35 | return true;
36 | }
37 |
38 | /**__DEBUG/
39 | if (x.$ === 'Set_gren_builtin')
40 | {
41 | x = __Set_toArray(x);
42 | y = __Set_toArray(y);
43 | }
44 | if (x.$ === 'RBNode_gren_builtin' || x.$ === 'RBEmpty_gren_builtin')
45 | {
46 | x = A3(__Dict_foldl, F3(function(key, value, acc) { acc.push({ a: key, b: value }); return acc; }), [], x);
47 | y = A3(__Dict_foldl, F3(function(key, value, acc) { acc.push({ a: key, b: value }); return acc; }), [], y);
48 | }
49 | //*/
50 |
51 | /**__PROD/
52 | if (x.$ < 0)
53 | {
54 | x = A3(__Dict_foldl, F3(function(key, value, acc) { acc.push({ a: key, b: value }); return acc; }), [], x);
55 | y = A3(__Dict_foldl, F3(function(key, value, acc) { acc.push({ a: key, b: value }); return acc; }), [], y);
56 | }
57 | //*/
58 |
59 | if (x instanceof DataView) {
60 | var length = x.byteLength;
61 |
62 | if (y.byteLength !== length) {
63 | return false;
64 | }
65 |
66 | for (var i = 0; i < length; ++i) {
67 | if (x.getUint8(i) !== y.getUint8(i)) {
68 | return false;
69 | }
70 | }
71 |
72 | return true;
73 | }
74 |
75 | if (x instanceof _Array_Builder) {
76 | x = __Array_fromBuilder(x);
77 | y = __Array_fromBuilder(y);
78 | }
79 |
80 | if (Array.isArray(x) && x.length !== y.length) {
81 | return false;
82 | }
83 |
84 | var nextDepth = depth + 1;
85 |
86 | for (var key in x) {
87 | if (!_Utils_eqHelp(x[key], y[key], nextDepth, stack)) {
88 | return false;
89 | }
90 | }
91 |
92 | return true;
93 | }
94 |
95 | var _Utils_equal = F2(_Utils_eq);
96 | var _Utils_notEqual = F2(function (a, b) {
97 | return !_Utils_eq(a, b);
98 | });
99 |
100 | // COMPARISONS
101 |
102 | // Code in Generate/JavaScript.hs, Basics.js, and depends on
103 | // the particular integer values assigned to LT, EQ, and GT.
104 |
105 | function _Utils_cmp(x, y) {
106 | if (typeof x !== "object") {
107 | return x === y ? /*EQ*/ 0 : x < y ? /*LT*/ -1 : /*GT*/ 1;
108 | }
109 |
110 | /**__DEBUG/
111 | if (x instanceof String)
112 | {
113 | var a = x.valueOf();
114 | var b = y.valueOf();
115 | return a === b ? 0 : a < b ? -1 : 1;
116 | }
117 | //*/
118 |
119 | // At this point, we can only be comparing arrays
120 | for (var idx = 0; idx < x.length; idx++) {
121 | var ord = _Utils_cmp(x[idx], y[idx]);
122 | if (ord !== 0) return ord;
123 | }
124 |
125 | return x.length - y.length;
126 | }
127 |
128 | var _Utils_lt = F2(function (a, b) {
129 | return _Utils_cmp(a, b) < 0;
130 | });
131 | var _Utils_le = F2(function (a, b) {
132 | return _Utils_cmp(a, b) < 1;
133 | });
134 | var _Utils_gt = F2(function (a, b) {
135 | return _Utils_cmp(a, b) > 0;
136 | });
137 | var _Utils_ge = F2(function (a, b) {
138 | return _Utils_cmp(a, b) >= 0;
139 | });
140 |
141 | var _Utils_compare = F2(function (x, y) {
142 | var n = _Utils_cmp(x, y);
143 | return n < 0 ? __Basics_LT : n ? __Basics_GT : __Basics_EQ;
144 | });
145 |
146 | // COMMON VALUES
147 |
148 | function _Utils_chr__PROD(c) {
149 | return c;
150 | }
151 | function _Utils_chr__DEBUG(c) {
152 | return new String(c);
153 | }
154 |
155 | // RECORDS
156 |
157 | function _Utils_update(oldRecord, updatedFields) {
158 | var newRecord = {};
159 |
160 | for (var key in oldRecord) {
161 | newRecord[key] = oldRecord[key];
162 | }
163 |
164 | for (var key in updatedFields) {
165 | newRecord[key] = updatedFields[key];
166 | }
167 |
168 | return newRecord;
169 | }
170 |
171 | // APPEND
172 |
173 | var _Utils_append = F2(_Utils_ap);
174 |
175 | function _Utils_ap(xs, ys) {
176 | // append Strings
177 | if (typeof xs === "string") {
178 | return xs + ys;
179 | }
180 |
181 | return xs.concat(ys);
182 | }
183 |
--------------------------------------------------------------------------------
/src/Json/Encode.gren:
--------------------------------------------------------------------------------
1 | module Json.Encode exposing
2 | ( encode, Value
3 | , string, int, float, bool, null
4 | , array, set
5 | , object, dict
6 | )
7 |
8 | {-| Functions for turning Gren values into Json values.
9 |
10 |
11 | @docs encode, Value
12 |
13 |
14 | ## Primitives
15 |
16 | @docs string, int, float, bool, null
17 |
18 |
19 | ## Arrays
20 |
21 | @docs array, set
22 |
23 |
24 | ## Objects
25 |
26 | @docs object, dict
27 |
28 | -}
29 |
30 | import Basics exposing (..)
31 | import Array exposing (Array)
32 | import Dict exposing (Dict)
33 | import Set exposing (Set)
34 | import String exposing (String)
35 | import Gren.Kernel.Json
36 |
37 |
38 |
39 | -- ENCODE
40 |
41 |
42 | {-| Represents a JavaScript value.
43 | -}
44 | type Value
45 | = Value
46 |
47 |
48 | {-| Convert a `Value` into a prettified string. The first argument specifies
49 | the amount of indentation in the resulting string.
50 |
51 | import Json.Encode as Encode
52 |
53 | tom : Encode.Value
54 | tom =
55 | Encode.object
56 | [ { key = "name", value = Encode.string "Tom" }
57 | , { key = "age", value = Encode.int 42 )
58 | ]
59 |
60 | compact =
61 | Encode.encode 0 tom
62 |
63 | -- {"name":"Tom","age":42}
64 | readable =
65 | Encode.encode 4 tom
66 |
67 | -- {
68 | -- "name": "Tom",
69 | -- "age": 42
70 | -- }
71 |
72 | -}
73 | encode : Int -> Value -> String
74 | encode =
75 | Gren.Kernel.Json.encode
76 |
77 |
78 |
79 | -- PRIMITIVES
80 |
81 |
82 | {-| Turn a `String` into a JSON string.
83 |
84 | import Json.Encode exposing (encode, string)
85 |
86 |
87 | -- encode 0 (string "") == "\"\""
88 | -- encode 0 (string "abc") == "\"abc\""
89 | -- encode 0 (string "hello") == "\"hello\""
90 |
91 | -}
92 | string : String -> Value
93 | string =
94 | Gren.Kernel.Json.wrap
95 |
96 |
97 | {-| Turn an `Int` into a JSON number.
98 |
99 | import Json.Encode exposing (encode, int)
100 |
101 |
102 | -- encode 0 (int 42) == "42"
103 | -- encode 0 (int -7) == "-7"
104 | -- encode 0 (int 0) == "0"
105 |
106 | -}
107 | int : Int -> Value
108 | int =
109 | Gren.Kernel.Json.wrap
110 |
111 |
112 | {-| Turn a `Float` into a JSON number.
113 |
114 | import Json.Encode exposing (encode, float)
115 |
116 |
117 | -- encode 0 (float 3.14) == "3.14"
118 | -- encode 0 (float 1.618) == "1.618"
119 | -- encode 0 (float -42) == "-42"
120 | -- encode 0 (float NaN) == "null"
121 | -- encode 0 (float Infinity) == "null"
122 |
123 | **Note:** Floating point numbers are defined in the [IEEE 754 standard][ieee]
124 | which is hardcoded into almost all CPUs. This standard allows `Infinity` and
125 | `NaN`. [The JSON spec][json] does not include these values, so we encode them
126 | both as `null`.
127 |
128 | [ieee]: https://en.wikipedia.org/wiki/IEEE_754
129 | [json]: https://www.json.org/
130 |
131 | -}
132 | float : Float -> Value
133 | float =
134 | Gren.Kernel.Json.wrap
135 |
136 |
137 | {-| Turn a `Bool` into a JSON boolean.
138 |
139 | import Json.Encode exposing (bool, encode)
140 |
141 |
142 | -- encode 0 (bool True) == "true"
143 | -- encode 0 (bool False) == "false"
144 |
145 | -}
146 | bool : Bool -> Value
147 | bool =
148 | Gren.Kernel.Json.wrap
149 |
150 |
151 |
152 | -- NULLS
153 |
154 |
155 | {-| Create a JSON `null` value.
156 |
157 | import Json.Encode exposing (encode, null)
158 |
159 |
160 | -- encode 0 null == "null"
161 |
162 | -}
163 | null : Value
164 | null =
165 | Gren.Kernel.Json.encodeNull
166 |
167 |
168 |
169 | -- ARRAYS
170 |
171 |
172 | {-| Turn a `Array` into a JSON array.
173 |
174 | import Json.Encode as Encode exposing (array, bool, encode, int, string)
175 |
176 |
177 | -- encode 0 (array int [1,3,4]) == "[1,3,4]"
178 | -- encode 0 (array bool [True,False]) == "[true,false]"
179 | -- encode 0 (array string ["a","b"]) == """["a","b"]"""
180 |
181 | -}
182 | array : (a -> Value) -> Array a -> Value
183 | array func entries =
184 | Gren.Kernel.Json.wrap
185 | (Array.foldl (Gren.Kernel.Json.addEntry func) (Gren.Kernel.Json.emptyArray {}) entries)
186 |
187 |
188 | {-| Turn an `Set` into a JSON array.
189 | -}
190 | set : (a -> Value) -> Set a -> Value
191 | set func entries =
192 | Gren.Kernel.Json.wrap
193 | (Set.foldl (Gren.Kernel.Json.addEntry func) (Gren.Kernel.Json.emptyArray {}) entries)
194 |
195 |
196 |
197 | -- OBJECTS
198 |
199 |
200 | {-| Create a JSON object.
201 |
202 | import Json.Encode as Encode
203 |
204 | tom : Encode.Value
205 | tom =
206 | Encode.object
207 | [ { key = "name", value = Encode.string "Tom" }
208 | , { key = "age", value = Encode.int 42 }
209 | ]
210 |
211 | -- Encode.encode 0 tom == """{"name":"Tom","age":42}"""
212 |
213 | -}
214 | object : Array { key : String, value : Value } -> Value
215 | object pairs =
216 | Gren.Kernel.Json.wrap
217 | (Array.foldl
218 | (\{ key, value } obj -> Gren.Kernel.Json.addField key value obj)
219 | (Gren.Kernel.Json.emptyObject {})
220 | pairs
221 | )
222 |
223 |
224 | {-| Turn a `Dict` into a JSON object.
225 |
226 | import Dict exposing (Dict)
227 | import Json.Encode as Encode
228 |
229 | people : Dict String Int
230 | people =
231 | Dict.fromArray [ { key = "Tom", value = 42 }, { key = "Sue", value = 38 } ]
232 |
233 | -- Encode.encode 0 (Encode.dict identity Encode.int people)
234 | -- == """{"Tom":42,"Sue":38}"""
235 |
236 | -}
237 | dict : (k -> String) -> (v -> Value) -> Dict k v -> Value
238 | dict toKey toValue dictionary =
239 | Gren.Kernel.Json.wrap
240 | (Dict.foldl
241 | (\key value obj -> Gren.Kernel.Json.addField (toKey key) (toValue value) obj)
242 | (Gren.Kernel.Json.emptyObject {})
243 | dictionary
244 | )
245 |
--------------------------------------------------------------------------------
/src/Math.gren:
--------------------------------------------------------------------------------
1 | module Math exposing
2 | ( round, floor, ceiling, truncate
3 | , modBy, remainderBy, abs, sqrt, logBase
4 | , e, pi, maxSafeInteger, minSafeInteger, maxFloat, minFloat
5 | , degrees, radians, turns
6 | , cos, sin, tan, acos, asin, atan, atan2
7 | )
8 |
9 | {-| Functions for doing math
10 |
11 | @docs round, floor, ceiling, truncate, modBy, remainderBy, abs, sqrt, logBase
12 |
13 |
14 | ## Constants
15 |
16 | @docs e, pi, maxSafeInteger, minSafeInteger, maxFloat, minFloat
17 |
18 |
19 | ## Angles
20 |
21 | @docs degrees, radians, turns
22 |
23 |
24 | ## Trigonometry
25 |
26 | @docs cos, sin, tan, acos, asin, atan, atan2
27 |
28 |
29 | -}
30 |
31 | import Basics exposing (Int, Float, (==), (/), (*), (<))
32 | import Gren.Kernel.Math
33 |
34 |
35 | {-| Round a number to the nearest integer.
36 |
37 | round 1.0 == 1
38 |
39 | round 1.2 == 1
40 |
41 | round 1.5 == 2
42 |
43 | round 1.8 == 2
44 |
45 | round -1.2 == -1
46 |
47 | round -1.5 == -1
48 |
49 | round -1.8 == -2
50 |
51 | -}
52 | round : Float -> Int
53 | round =
54 | Gren.Kernel.Math.round
55 |
56 |
57 | {-| Floor function, rounding down.
58 |
59 | floor 1.0 == 1
60 |
61 | floor 1.2 == 1
62 |
63 | floor 1.5 == 1
64 |
65 | floor 1.8 == 1
66 |
67 | floor -1.2 == -2
68 |
69 | floor -1.5 == -2
70 |
71 | floor -1.8 == -2
72 |
73 | -}
74 | floor : Float -> Int
75 | floor =
76 | Gren.Kernel.Math.floor
77 |
78 |
79 | {-| Ceiling function, rounding up.
80 |
81 | ceiling 1.0 == 1
82 |
83 | ceiling 1.2 == 2
84 |
85 | ceiling 1.5 == 2
86 |
87 | ceiling 1.8 == 2
88 |
89 | ceiling -1.2 == -1
90 |
91 | ceiling -1.5 == -1
92 |
93 | ceiling -1.8 == -1
94 |
95 | -}
96 | ceiling : Float -> Int
97 | ceiling =
98 | Gren.Kernel.Math.ceiling
99 |
100 |
101 | {-| Truncate a number, rounding towards zero.
102 |
103 | truncate 1.0 == 1
104 |
105 | truncate 1.2 == 1
106 |
107 | truncate 1.5 == 1
108 |
109 | truncate 1.8 == 1
110 |
111 | truncate -1.2 == -1
112 |
113 | truncate -1.5 == -1
114 |
115 | truncate -1.8 == -1
116 |
117 | -}
118 | truncate : Float -> Int
119 | truncate =
120 | Gren.Kernel.Math.truncate
121 |
122 |
123 | {-| Perform [modular arithmetic](https://en.wikipedia.org/wiki/Modular_arithmetic).
124 | A common trick is to use (n mod 2) to detect even and odd numbers:
125 |
126 | modBy 2 0 == 0
127 |
128 | modBy 2 1 == 1
129 |
130 | modBy 2 2 == 0
131 |
132 | modBy 2 3 == 1
133 |
134 | Our `modBy` function works in the typical mathematical way when you run into
135 | negative numbers:
136 |
137 | List.map (modBy 4) [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ]
138 | -- [ 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1 ]
139 |
140 | Use [`remainderBy`](#remainderBy) for a different treatment of negative numbers,
141 | or read Daan Leijen’s [Division and Modulus for Computer Scientists][dm] for more
142 | information.
143 |
144 | [dm]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
145 |
146 | -}
147 | modBy : Int -> Int -> Int
148 | modBy =
149 | Gren.Kernel.Math.modBy
150 |
151 |
152 | {-| Get the remainder after division. Here are bunch of examples of dividing by four:
153 |
154 | List.map (remainderBy 4) [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ]
155 | -- [ -1, 0, -3, -2, -1, 0, 1, 2, 3, 0, 1 ]
156 |
157 | Use [`modBy`](#modBy) for a different treatment of negative numbers,
158 | or read Daan Leijen’s [Division and Modulus for Computer Scientists][dm] for more
159 | information.
160 |
161 | [dm]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
162 |
163 | -}
164 | remainderBy : Int -> Int -> Int
165 | remainderBy =
166 | Gren.Kernel.Math.remainderBy
167 |
168 |
169 | {-| Get the [absolute value][abs] of a number.
170 |
171 | abs 16 == 16
172 |
173 | abs -4 == 4
174 |
175 | abs -8.5 == 8.5
176 |
177 | abs 3.14 == 3.14
178 |
179 | [abs]: https://en.wikipedia.org/wiki/Absolute_value
180 |
181 | -}
182 | abs : number -> number
183 | abs n =
184 | if n < 0 then
185 | -n
186 |
187 | else
188 | n
189 |
190 | {-| Take the square root of a number.
191 |
192 | sqrt 4 == 2
193 |
194 | sqrt 9 == 3
195 |
196 | sqrt 16 == 4
197 |
198 | sqrt 25 == 5
199 |
200 | -}
201 | sqrt : Float -> Float
202 | sqrt =
203 | Gren.Kernel.Math.sqrt
204 |
205 |
206 | {-| Calculate the logarithm of a number with a given base.
207 |
208 | logBase 10 100 == 2
209 |
210 | logBase 2 256 == 8
211 |
212 | -}
213 | logBase : Float -> Float -> Float
214 | logBase base number =
215 | if base == 10 then
216 | Gren.Kernel.Math.log10 number
217 |
218 | else
219 | (Gren.Kernel.Math.log number) / (Gren.Kernel.Math.log base)
220 |
221 |
222 | -- ANGLES
223 |
224 |
225 | {-| Convert radians to standard Gren angles (radians).
226 |
227 | radians pi == 3.141592653589793
228 |
229 | -}
230 | radians : Float -> Float
231 | radians angleInRadians =
232 | angleInRadians
233 |
234 |
235 | {-| Convert degrees to standard Gren angles (radians).
236 |
237 | degrees 180 == 3.141592653589793
238 |
239 | -}
240 | degrees : Float -> Float
241 | degrees angleInDegrees =
242 | (angleInDegrees * pi) / 180
243 |
244 |
245 | {-| Convert turns to standard Gren angles (radians). One turn is equal to 360°.
246 |
247 | turns (1 / 2) == 3.141592653589793
248 |
249 | -}
250 | turns : Float -> Float
251 | turns angleInTurns =
252 | (2 * pi) * angleInTurns
253 |
254 |
255 | -- CONSTANTS
256 |
257 |
258 | {-| An approximation of e.
259 | -}
260 | e : Float
261 | e =
262 | Gren.Kernel.Math.e
263 |
264 |
265 | {-| An approximation of pi.
266 | -}
267 | pi : Float
268 | pi =
269 | Gren.Kernel.Math.pi
270 |
271 |
272 | {-| The largest integer value that can be exactly represented and compared in a JavaScript environment.
273 | Integers above this value may not work as you expect.
274 | -}
275 | maxSafeInteger : Int
276 | maxSafeInteger =
277 | Gren.Kernel.Math.maxSafeInteger
278 |
279 |
280 | {-| The smallest integer value that can be exactly represented and compared in a JavaScript environment.
281 | Integers below this value may not work as you expect.
282 | -}
283 | minSafeInteger : Int
284 | minSafeInteger =
285 | Gren.Kernel.Math.minSafeInteger
286 |
287 |
288 | {-| The largest `Float` value.
289 | -}
290 | maxFloat : Float
291 | maxFloat =
292 | Gren.Kernel.Math.maxFloat
293 |
294 |
295 | {-| The smallest `Float` value.
296 | -}
297 | minFloat : Float
298 | minFloat =
299 | -maxFloat
300 |
301 |
302 | -- TRIGONOMETRY
303 |
304 |
305 | {-| Figure out the cosine given an angle in radians.
306 |
307 | cos (degrees 60) == 0.5000000000000001
308 |
309 | cos (turns (1 / 6)) == 0.5000000000000001
310 |
311 | cos (radians (pi / 3)) == 0.5000000000000001
312 |
313 | cos (pi / 3) == 0.5000000000000001
314 |
315 | -}
316 | cos : Float -> Float
317 | cos =
318 | Gren.Kernel.Math.cos
319 |
320 |
321 | {-| Figure out the sine given an angle in radians.
322 |
323 | sin (degrees 30) == 0.49999999999999994
324 |
325 | sin (turns (1 / 12)) == 0.49999999999999994
326 |
327 | sin (radians (pi / 6)) == 0.49999999999999994
328 |
329 | sin (pi / 6) == 0.49999999999999994
330 |
331 | -}
332 | sin : Float -> Float
333 | sin =
334 | Gren.Kernel.Math.sin
335 |
336 |
337 | {-| Figure out the tangent given an angle in radians.
338 |
339 | tan (degrees 45) == 0.9999999999999999
340 |
341 | tan (turns (1 / 8)) == 0.9999999999999999
342 |
343 | tan (radians (pi / 4)) == 0.9999999999999999
344 |
345 | tan (pi / 4) == 0.9999999999999999
346 |
347 | -}
348 | tan : Float -> Float
349 | tan =
350 | Gren.Kernel.Math.tan
351 |
352 |
353 | {-| Figure out the arccosine for `adjacent / hypotenuse` in radians:
354 |
355 | acos (1 / 2) == 1.0471975511965979 -- 60° or pi/3 radians
356 |
357 | -}
358 | acos : Float -> Float
359 | acos =
360 | Gren.Kernel.Math.acos
361 |
362 |
363 | {-| Figure out the arcsine for `opposite / hypotenuse` in radians:
364 |
365 | asin (1 / 2) == 0.5235987755982989 -- 30° or pi/6 radians
366 |
367 | -}
368 | asin : Float -> Float
369 | asin =
370 | Gren.Kernel.Math.asin
371 |
372 |
373 | {-| This helps you find the angle (in radians) to an `(x,y)` coordinate, but
374 | in a way that is rarely useful in programming. **You probably want
375 | [`atan2`](#atan2) instead!**
376 |
377 | This version takes `y/x` as its argument, so there is no way to know whether
378 | the negative signs comes from the `y` or `x` value. So as we go counter-clockwise
379 | around the origin from point `(1,1)` to `(1,-1)` to `(-1,-1)` to `(-1,1)` we do
380 | not get angles that go in the full circle:
381 |
382 | atan (1 / 1) == 0.7853981633974483 -- 45° or pi/4 radians
383 |
384 | atan (1 / -1) == -0.7853981633974483 -- 315° or 7*pi/4 radians
385 |
386 | atan (-1 / -1) == 0.7853981633974483 -- 45° or pi/4 radians
387 |
388 | atan (-1 / 1) == -0.7853981633974483 -- 315° or 7*pi/4 radians
389 |
390 | Notice that everything is between `pi/2` and `-pi/2`. That is pretty useless
391 | for figuring out angles in any sort of visualization, so again, check out
392 | [`atan2`](#atan2) instead!
393 |
394 | -}
395 | atan : Float -> Float
396 | atan =
397 | Gren.Kernel.Math.atan
398 |
399 |
400 | {-| This helps you find the angle (in radians) to an `(x,y)` coordinate.
401 | So rather than saying `atan (y/x)` you say `atan2 y x` and you can get a full
402 | range of angles:
403 |
404 | atan2 1 1 == 0.7853981633974483 -- 45° or pi/4 radians
405 |
406 | atan2 1 -1 == 2.356194490192345 -- 135° or 3*pi/4 radians
407 |
408 | atan2 -1 -1 == -2.356194490192345 -- 225° or 5*pi/4 radians
409 |
410 | atan2 -1 1 == -0.7853981633974483 -- 315° or 7*pi/4 radians
411 |
412 | -}
413 | atan2 : Float -> Float -> Float
414 | atan2 =
415 | Gren.Kernel.Math.atan2
416 |
--------------------------------------------------------------------------------
/src/Maybe.gren:
--------------------------------------------------------------------------------
1 | module Maybe exposing
2 | ( Maybe(..)
3 | , hasValue, checkValue, keepIf
4 | , withDefault, withDefaultLazy, map, map2, map3, andMap
5 | , andThen
6 | )
7 |
8 | {-| This library fills a bunch of important niches in Gren. A `Maybe` can help
9 | you with optional arguments, error handling, and records with optional fields.
10 |
11 | @docs Maybe
12 |
13 |
14 | ## Queries
15 |
16 | @docs hasValue, checkValue, keepIf
17 |
18 |
19 | ## Transform
20 |
21 | @docs withDefault, withDefaultLazy, map, map2, map3, andMap, andThen
22 |
23 | -}
24 |
25 | import Basics exposing (..)
26 |
27 |
28 | {-| Represent values that may or may not exist. It can be useful if you have a
29 | record field that is only filled in sometimes. Or if a function takes a value
30 | sometimes, but does not absolutely need it.
31 |
32 | -- A person, but maybe we do not know their age.
33 | type alias Person =
34 | { name : String
35 | , age : Maybe Int
36 | }
37 |
38 | tom =
39 | { name = "Tom", age = Just 42 }
40 |
41 | sue =
42 | { name = "Sue", age = Nothing }
43 |
44 | -}
45 | type Maybe a
46 | = Just a
47 | | Nothing
48 |
49 |
50 | {-| Checks to see if the [Maybe](#Maybe) is `Just`, and that the contained value
51 | equals a provided constant.
52 |
53 | hasValue 5 (Just 5) == True
54 |
55 | hasValue 5 (Just 3) == False
56 |
57 | hasValue 5 Nothing == False
58 |
59 | -}
60 | hasValue : a -> Maybe a -> Bool
61 | hasValue value maybe =
62 | when maybe is
63 | Just contained ->
64 | contained == value
65 |
66 | Nothing ->
67 | False
68 |
69 |
70 | {-| Checks to see if the [Maybe](#Maybe) is `Just`, and that the contained value
71 | passes the provided test.
72 |
73 | checkValue isOdd (Just 5) == True
74 |
75 | checkValue isOdd (Just 2) == False
76 |
77 | checkValue isOdd Nothing == False
78 |
79 | -}
80 | checkValue : (a -> Bool) -> Maybe a -> Bool
81 | checkValue test maybe =
82 | when maybe is
83 | Just contained ->
84 | test contained
85 |
86 | Nothing ->
87 | False
88 |
89 |
90 | {-| Provide a default value, turning an optional value into a normal
91 | value. This comes in handy when paired with functions like
92 | [`Dict.get`](Dict#get) which gives back a `Maybe`.
93 |
94 | withDefault 100 (Just 42) == 42
95 |
96 | withDefault 100 Nothing == 100
97 |
98 | withDefault "unknown" (Dict.get "Tom" Dict.empty) == "unknown"
99 |
100 | -}
101 | withDefault : a -> Maybe a -> a
102 | withDefault default maybe =
103 | when maybe is
104 | Just value ->
105 | value
106 |
107 | Nothing ->
108 | default
109 |
110 |
111 | {-| Same as [withDefault](#withDefault) but the default value is wrapped in
112 | a function. This is useful when computing the default value is expensive, as
113 | you can compute it only when it is required.
114 |
115 | In most cases you should use pattern matching or [withDefault](#withDefault) instead.
116 |
117 | -}
118 | withDefaultLazy : ({} -> a) -> Maybe a -> a
119 | withDefaultLazy default maybe =
120 | when maybe is
121 | Just value ->
122 | value
123 |
124 | Nothing ->
125 | default {}
126 |
127 |
128 | {-| Transform a `Maybe` value with a given function:
129 |
130 | map sqrt (Just 9) == Just 3
131 |
132 | map sqrt Nothing == Nothing
133 |
134 | map sqrt (String.toFloat "9") == Just 3
135 |
136 | map sqrt (String.toFloat "x") == Nothing
137 |
138 | -}
139 | map : (a -> b) -> Maybe a -> Maybe b
140 | map f maybe =
141 | when maybe is
142 | Just value ->
143 | Just (f value)
144 |
145 | Nothing ->
146 | Nothing
147 |
148 |
149 | {-| Apply a function if both arguments are `Just` a value.
150 |
151 | map2 (+) (Just 3) (Just 4) == Just 7
152 |
153 | map2 (+) (Just 3) Nothing == Nothing
154 |
155 | map2 (+) Nothing (Just 4) == Nothing
156 |
157 | map2 (+) (String.toInt "1") (String.toInt "123") == Just 124
158 |
159 | map2 (+) (String.toInt "x") (String.toInt "123") == Nothing
160 |
161 | map2 (+) (String.toInt "1") (String.toInt "1.3") == Nothing
162 |
163 | -}
164 | map2 : (a -> b -> value) -> Maybe a -> Maybe b -> Maybe value
165 | map2 func ma mb =
166 | when ma is
167 | Nothing ->
168 | Nothing
169 |
170 | Just a ->
171 | when mb is
172 | Nothing ->
173 | Nothing
174 |
175 | Just b ->
176 | Just (func a b)
177 |
178 |
179 | {-| Apply a function if all three arguments are `Just` a value.
180 |
181 | map3 (+) (Just 3) (Just 4) (Just 3) == Just 10
182 |
183 | map3 (+) (Just 3) Nothing (Just 3) == Nothing
184 |
185 | map3 (+) Nothing (Just 4) (Just 3) == Nothing
186 |
187 | map3 (+) (String.toInt "1") (String.toInt "123") (String.toInt "4") == Just 128
188 |
189 | map3 (+) (String.toInt "x") (String.toInt "123") (String.toInt "4") == Nothing
190 |
191 | -}
192 | map3 : (a -> b -> c -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value
193 | map3 func ma mb mc =
194 | when ma is
195 | Nothing ->
196 | Nothing
197 |
198 | Just a ->
199 | when mb is
200 | Nothing ->
201 | Nothing
202 |
203 | Just b ->
204 | when mc is
205 | Nothing ->
206 | Nothing
207 |
208 | Just c ->
209 | Just (func a b c)
210 |
211 |
212 | {-| Combine multiple `Maybe`s. Helpful when [map2](#map2) and [map3](#map3) aren't enough.
213 |
214 | Just (\a b c -> a ++ b ++ c)
215 | |> andMap (Just "one ")
216 | |> andMap (Just "more ")
217 | |> andMap (Just "time")
218 | == (Just "one more time")
219 | -}
220 | andMap : Maybe a -> Maybe (a -> b) -> Maybe b
221 | andMap =
222 | map2 (|>)
223 |
224 |
225 | {-| Returns `Nothing` if the contained value doesn't pass the given
226 | test.
227 |
228 | keepIf isOdd (Just 5) == Just 5
229 |
230 | keepIf isOdd (Just 2) == Nothing
231 |
232 | -}
233 | keepIf : (a -> Bool) -> Maybe a -> Maybe a
234 | keepIf test maybe =
235 | when maybe is
236 | Just contained ->
237 | if test contained then
238 | maybe
239 |
240 | else
241 | Nothing
242 |
243 | Nothing ->
244 | Nothing
245 |
246 |
247 | {-| Chain together many computations that may fail. It is helpful to see its
248 | definition:
249 |
250 | andThen : (a -> Maybe b) -> Maybe a -> Maybe b
251 | andThen callback maybe =
252 | when maybe is
253 | Just value ->
254 | callback value
255 |
256 | Nothing ->
257 | Nothing
258 |
259 | This means we only continue with the callback if things are going well. For
260 | example, say you need to parse some user input as a month:
261 |
262 | parseMonth : String -> Maybe Int
263 | parseMonth userInput =
264 | String.toInt userInput
265 | |> andThen toValidMonth
266 |
267 | toValidMonth : Int -> Maybe Int
268 | toValidMonth month =
269 | if 1 <= month && month <= 12 then
270 | Just month
271 |
272 | else
273 | Nothing
274 |
275 | In the `parseMonth` function, if `String.toInt` produces `Nothing` (because
276 | the `userInput` was not an integer) this entire chain of operations will
277 | short-circuit and result in `Nothing`. If `toValidMonth` results in `Nothing`,
278 | again the chain of computations will result in `Nothing`.
279 |
280 | -}
281 | andThen : (a -> Maybe b) -> Maybe a -> Maybe b
282 | andThen callback maybeValue =
283 | when maybeValue is
284 | Just value ->
285 | callback value
286 |
287 | Nothing ->
288 | Nothing
289 |
290 |
291 |
292 | -- FOR INTERNAL USE ONLY
293 | --
294 | -- Use `when` expressions for this in Gren code!
295 |
296 |
297 | isJust : Maybe a -> Bool
298 | isJust maybe =
299 | when maybe is
300 | Just _ ->
301 | True
302 |
303 | Nothing ->
304 | False
305 |
306 |
307 | destruct : b -> (a -> b) -> Maybe a -> b
308 | destruct default func maybe =
309 | when maybe is
310 | Just a ->
311 | func a
312 |
313 | Nothing ->
314 | default
315 |
--------------------------------------------------------------------------------
/src/Platform.gren:
--------------------------------------------------------------------------------
1 | module Platform exposing
2 | ( Program, worker
3 | , Task, ProcessId
4 | , Router, sendToApp, sendToSelf
5 | )
6 |
7 | {-| This module contains definitions important to the language runtime.
8 | You're unlikely to make direct use of these things yourself.
9 |
10 |
11 | @docs Program, worker
12 |
13 |
14 | ## Tasks and Processes
15 |
16 | @docs Task, ProcessId
17 |
18 |
19 | ## Effect Manager Helpers
20 |
21 | Effect managers can be viewed as programs-within-a-program. They have their own
22 | state, and communicate with the application using messages.
23 |
24 | Effect managers are used internally for many things, but isn't considered to be
25 | truly stable. It's likely that this feature will be redesigned in a future relase.
26 |
27 |
28 | @docs Router, sendToApp, sendToSelf
29 |
30 | -}
31 |
32 | import Basics exposing (Never)
33 | import Gren.Kernel.Platform
34 | import Gren.Kernel.Scheduler
35 | import Platform.Cmd exposing (Cmd)
36 | import Platform.Sub exposing (Sub)
37 |
38 |
39 |
40 | -- PROGRAMS
41 |
42 |
43 | {-| A `Program` describes an Gren program! How does it react to input? Does it
44 | show anything on screen? Etc.
45 | -}
46 | type Program flags model msg
47 | = Program
48 |
49 |
50 | {-| Create a [headless] program with no user interface.
51 |
52 | This is great if you want to use Gren as the “brain” for something
53 | else. For example, you could send messages out ports to modify the DOM, but do
54 | all the complex logic in Gren.
55 |
56 | [headless]: https://en.wikipedia.org/wiki/Headless_software
57 |
58 | Initializing a headless program from JavaScript looks like this:
59 |
60 | ```javascript
61 | var app = Gren.MyThing.init();
62 | ```
63 |
64 | If you _do_ want to control the user interface in Gren, the [`Browser`][browser]
65 | module has a few ways to create that kind of `Program` instead!
66 |
67 | [headless]: https://en.wikipedia.org/wiki/Headless_software
68 | [browser]: /package/gren-lang/browser/latest/module/Browser
69 |
70 | -}
71 | worker :
72 | { init : flags -> { model : model, command : Cmd msg }
73 | , update : msg -> model -> { model : model, command : Cmd msg }
74 | , subscriptions : model -> Sub msg
75 | }
76 | -> Program flags model msg
77 | worker =
78 | Gren.Kernel.Platform.worker
79 |
80 |
81 |
82 | -- TASKS and PROCESSES
83 |
84 |
85 | {-| Head over to the documentation for the [`Task`](Task) module for more
86 | information on this. It is only defined here because it is a platform
87 | primitive.
88 | -}
89 | type Task err ok
90 | = Task
91 |
92 |
93 | {-| Head over to the documentation for the [`Process`](Process) module for
94 | information on this. It is only defined here because it is a platform
95 | primitive.
96 | -}
97 | type ProcessId
98 | = ProcessId
99 |
100 |
101 |
102 | -- EFFECT MANAGER INTERNALS
103 |
104 |
105 | {-| An effect manager has access to a “router” that routes messages between
106 | the main app and your individual effect manager.
107 | -}
108 | type Router appMsg selfMsg
109 | = Router
110 |
111 |
112 | {-| Send the router a message for the main loop of your app. This message will
113 | be handled by the overall `update` function, just like events from `Html`.
114 | -}
115 | sendToApp : Router msg a -> msg -> Task x {}
116 | sendToApp =
117 | Gren.Kernel.Platform.sendToApp
118 |
119 |
120 | {-| Send the router a message for your effect manager. This message will
121 | be routed to the `onSelfMsg` function, where you can update the state of your
122 | effect manager as necessary.
123 |
124 | As an example, the effect manager for web sockets
125 |
126 | -}
127 | sendToSelf : Router a msg -> msg -> Task x {}
128 | sendToSelf =
129 | Gren.Kernel.Platform.sendToSelf
130 |
--------------------------------------------------------------------------------
/src/Platform/Cmd.gren:
--------------------------------------------------------------------------------
1 | module Platform.Cmd exposing
2 | ( Cmd, none, batch
3 | , map
4 | )
5 |
6 | {-|
7 |
8 | > **Note:** Gren has **managed effects**, meaning that things like HTTP
9 | > requests or writing to disk are all treated as _data_ in Gren. When this
10 | > data is given to the Gren runtime system, it can do some “query optimization”
11 | > before actually performing the effect. Perhaps unexpectedly, this managed
12 | > effects idea is the heart of why Gren is so nice for testing, reuse,
13 | > reproducibility, etc.
14 | >
15 | > Gren has two kinds of managed effects: commands and subscriptions.
16 |
17 |
18 | ## Commands
19 |
20 | @docs Cmd, none, batch
21 |
22 |
23 | ## Fancy Stuff
24 |
25 | @docs map
26 |
27 | -}
28 |
29 | import Array exposing (Array)
30 | import Gren.Kernel.Platform
31 |
32 |
33 |
34 | -- COMMANDS
35 |
36 |
37 | {-| A command is a way of telling Gren, “Hey, I want you to do this thing!”
38 | So if you want to send an HTTP request, you would need to command Gren to do it.
39 | Or if you wanted to ask for geolocation, you would need to command Gren to go
40 | get it.
41 |
42 | Every `Cmd` specifies (1) which effects you need access to and (2) the type of
43 | messages that will come back into your application.
44 |
45 | **Note:** Do not worry if this seems confusing at first! As with every Gren user
46 | ever, commands will make more sense as you work through [the Gren Architecture
47 | Tutorial](https://guide.gren-lang.org/architecture/) and see how they
48 | fit into a real application!
49 |
50 | -}
51 | type Cmd msg
52 | = Cmd
53 |
54 |
55 | {-| Tell the runtime that there are no commands.
56 | -}
57 | none : Cmd msg
58 | none =
59 | batch []
60 |
61 |
62 | {-| When you need the runtime system to perform a couple commands, you
63 | can batch them together. Each is handed to the runtime at the same time,
64 | and since each can perform arbitrary operations in the world, there are
65 | no ordering guarantees about the results.
66 |
67 | **Note:** `Cmd.none` and `Cmd.batch [ Cmd.none, Cmd.none ]` and `Cmd.batch []`
68 | all do the same thing.
69 |
70 | -}
71 | batch : Array (Cmd msg) -> Cmd msg
72 | batch =
73 | Gren.Kernel.Platform.batch
74 |
75 |
76 |
77 | -- FANCY STUFF
78 |
79 |
80 | {-| Transform the messages produced by a command.
81 | Very similar to [`Html.map`](/package/gren-lang/browser/latest/module/Html#map).
82 |
83 | This is very rarely useful in well-structured Gren code, so definitely read the
84 | section on [structure] in the guide before reaching for this!
85 |
86 | [structure]: https://guide.gren-lang.org/webapps/structure.html
87 |
88 | -}
89 | map : (a -> msg) -> Cmd a -> Cmd msg
90 | map =
91 | Gren.Kernel.Platform.map
92 |
--------------------------------------------------------------------------------
/src/Platform/Sub.gren:
--------------------------------------------------------------------------------
1 | module Platform.Sub exposing
2 | ( Sub, none, batch
3 | , map
4 | )
5 |
6 | {-|
7 |
8 | > **Note:** Gren has **managed effects**, meaning that things like HTTP
9 | > requests or writing to disk are all treated as _data_ in Gren. When this
10 | > data is given to the Gren runtime system, it can do some “query optimization”
11 | > before actually performing the effect. Perhaps unexpectedly, this managed
12 | > effects idea is the heart of why Gren is so nice for testing, reuse,
13 | > reproducibility, etc.
14 | >
15 | > Gren has two kinds of managed effects: commands and subscriptions.
16 |
17 |
18 | ## Subscriptions
19 |
20 | @docs Sub, none, batch
21 |
22 |
23 | ## Fancy Stuff
24 |
25 | @docs map
26 |
27 | -}
28 |
29 | import Array exposing (Array)
30 | import Gren.Kernel.Platform
31 |
32 |
33 |
34 | -- SUBSCRIPTIONS
35 |
36 |
37 | {-| A subscription is a way of telling Gren, “Hey, let me know if anything
38 | interesting happens over there!” So if you want to listen for messages on a web
39 | socket, you would tell Gren to create a subscription. If you want to get clock
40 | ticks, you would tell Gren to subscribe to that. The cool thing here is that
41 | this means _Gren_ manages all the details of subscriptions instead of _you_.
42 | So if a web socket goes down, _you_ do not need to manually reconnect with an
43 | exponential backoff strategy, _Gren_ does this all for you behind the scenes!
44 |
45 | Every `Sub` specifies (1) which effects you need access to and (2) the type of
46 | messages that will come back into your application.
47 |
48 | **Note:** Do not worry if this seems confusing at first! As with every Gren user
49 | ever, subscriptions will make more sense as you work through [the Gren Architecture
50 | Tutorial](https://guide.gren-lang.org/architecture/) and see how they fit
51 | into a real application!
52 |
53 | -}
54 | type Sub msg
55 | = Sub
56 |
57 |
58 | {-| Tell the runtime that there are no subscriptions.
59 | -}
60 | none : Sub msg
61 | none =
62 | batch []
63 |
64 |
65 | {-| When you need to subscribe to multiple things, you can create a `batch` of
66 | subscriptions.
67 |
68 | **Note:** `Sub.none` and `Sub.batch [ Sub.none, Sub.none ]` and
69 | `Sub.batch []` all do the same thing.
70 |
71 | -}
72 | batch : Array (Sub msg) -> Sub msg
73 | batch =
74 | Gren.Kernel.Platform.batch
75 |
76 |
77 |
78 | -- FANCY STUFF
79 |
80 |
81 | {-| Transform the messages produced by a subscription.
82 | Very similar to [`Html.map`](/package/gren-lang/browser/latest/module/Html#map).
83 |
84 | This is very rarely useful in well-structured Gren code, so definitely read the
85 | section on [structure] in the guide before reaching for this!
86 |
87 | [structure]: https://guide.gren-lang.org/webapps/structure.html
88 |
89 | -}
90 | map : (a -> msg) -> Sub a -> Sub msg
91 | map =
92 | Gren.Kernel.Platform.map
93 |
--------------------------------------------------------------------------------
/src/Process.gren:
--------------------------------------------------------------------------------
1 | module Process exposing (Id, spawn, sleep, kill)
2 |
3 | {-| A process is essentially a function running concurrently to your application.
4 |
5 |
6 | @docs Id, spawn, sleep, kill
7 |
8 |
9 | ## Future Plans
10 |
11 | Right now, this library is pretty sparse. For example, there is no public API
12 | for processes to communicate with each other. This is a really important
13 | ability, but it is also something that is extraordinarily easy to get wrong!
14 |
15 | I think the trend will be towards an Erlang style of concurrency, where every
16 | process has an “event queue” that anyone can send messages to. I currently
17 | think the API will be extended to be more like this:
18 |
19 | type Id exit msg
20 |
21 | spawn : Task exit a -> Task x (Id exit Never)
22 |
23 | kill : Id exit msg -> Task x {}
24 |
25 | send : Id exit msg -> msg -> Task x {}
26 |
27 | A process `Id` will have two type variables to make sure all communication is
28 | valid. The `exit` type describes the messages that are produced if the process
29 | fails because of user code. So if processes are linked and trapping errors,
30 | they will need to handle this. The `msg` type just describes what kind of
31 | messages this process can be sent by strangers.
32 |
33 | We shall see though! This is just a draft that does not cover nearly everything
34 | it needs to, so the long-term vision for concurrency in Gren will be rolling out
35 | slowly as I get more data and experience.
36 |
37 | I ask that people bullish on compiling to node.js keep this in mind. I think we
38 | can do better than the hopelessly bad concurrency model of node.js, and I hope
39 | the Gren community will be supportive of being more ambitious, even if it takes
40 | longer. That’s kind of what Gren is all about.
41 |
42 | -}
43 |
44 | import Basics exposing (Float, Never)
45 | import Gren.Kernel.Process
46 | import Gren.Kernel.Scheduler
47 | import Platform
48 | import Task exposing (Task)
49 |
50 |
51 | {-| A light-weight process that runs concurrently. You can use `spawn` to
52 | get a bunch of different tasks running in different processes. The Gren runtime
53 | will interleave their progress. So if a task is taking too long, we will pause
54 | it at an `andThen` and switch over to other stuff.
55 |
56 | **Note:** We make a distinction between _concurrency_ which means interleaving
57 | different sequences and _parallelism_ which means running different
58 | sequences at the exact same time. For example, a
59 | [time-sharing system](https://en.wikipedia.org/wiki/Time-sharing) is definitely
60 | concurrent, but not necessarily parallel. So even though JS runs within a
61 | single OS-level thread, Gren can still run things concurrently.
62 |
63 | -}
64 | type alias Id =
65 | Platform.ProcessId
66 |
67 |
68 | {-| Run a task in its own light-weight process. In the following example,
69 | `task1` and `task2` will be interleaved. If `task1` makes a long HTTP request
70 | or is just taking a long time, we can hop over to `task2` and do some work
71 | there.
72 |
73 | spawn task1
74 | |> Task.andThen (\_ -> spawn task2)
75 |
76 | **Note:** This creates a relatively restricted kind of `Process` because it
77 | cannot receive any messages. More flexibility for user-defined processes will
78 | come in a later release!
79 |
80 | -}
81 | spawn : Task x a -> Task y Id
82 | spawn =
83 | Gren.Kernel.Scheduler.spawn
84 |
85 |
86 | {-| Block progress on the current process for the given number of milliseconds.
87 | The JavaScript equivalent of this is [`setTimeout`][setTimeout] which lets you
88 | delay work until later.
89 |
90 | [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
91 |
92 | -}
93 | sleep : Float -> Task x {}
94 | sleep =
95 | Gren.Kernel.Process.sleep
96 |
97 |
98 | {-| Sometimes you `spawn` a process, but later decide it would be a waste to
99 | have it keep running and doing stuff. The `kill` function will force a process
100 | to bail on whatever task it is running. So if there is an HTTP request in
101 | flight, it will also abort the request.
102 | -}
103 | kill : Id -> Task x {}
104 | kill =
105 | Gren.Kernel.Scheduler.kill
106 |
--------------------------------------------------------------------------------
/src/Set.gren:
--------------------------------------------------------------------------------
1 | module Set exposing
2 | ( Set
3 | , empty, singleton, set, remove, toggle
4 | , isEmpty, member, count, first, last, findFirst, findLast, any, all
5 | , union, intersect, diff
6 | , toArray, fromArray
7 | , map, foldl, foldr, keepIf, mapAndKeepJust, partition
8 | )
9 |
10 | {-| A set of unique values. The values can be any comparable type. This
11 | includes `Int`, `Float`, `Time`, `Char`, `String`, and tuples or arrays
12 | of comparable types.
13 |
14 | Set, remove, and query operations all take _O(log n)_ time.
15 |
16 |
17 | @docs Set
18 |
19 |
20 | @docs empty, singleton, set, remove, toggle
21 |
22 |
23 | ## Query
24 |
25 | @docs isEmpty, member, count, first, last, findFirst, findLast, any, all
26 |
27 |
28 | ## Combine
29 |
30 | @docs union, intersect, diff
31 |
32 |
33 | ## Arrays
34 |
35 | @docs toArray, fromArray
36 |
37 |
38 | ## Transform
39 |
40 | @docs map, foldl, foldr, keepIf, mapAndKeepJust, partition
41 |
42 | -}
43 |
44 | import Array exposing (Array)
45 | import Basics exposing (..)
46 | import Dict
47 | import Maybe exposing (Maybe(..))
48 |
49 |
50 | {-| Represents a set of unique values. So `(Set Int)` is a set of integers and
51 | `(Set String)` is a set of strings.
52 | -}
53 | type Set t
54 | = Set_gren_builtin (Dict.Dict t {})
55 |
56 |
57 | {-| Create an empty set.
58 | -}
59 | empty : Set a
60 | empty =
61 | Set_gren_builtin Dict.empty
62 |
63 |
64 | {-| Create a set with one value.
65 | -}
66 | singleton : comparable -> Set comparable
67 | singleton key =
68 | Set_gren_builtin (Dict.singleton key {})
69 |
70 |
71 | {-| Set a value into a set.
72 | -}
73 | set : comparable -> Set comparable -> Set comparable
74 | set key (Set_gren_builtin dict) =
75 | Set_gren_builtin (Dict.set key {} dict)
76 |
77 |
78 | {-| Remove a value from a set. If the value is not found, no changes are made.
79 | -}
80 | remove : comparable -> Set comparable -> Set comparable
81 | remove key (Set_gren_builtin dict) =
82 | Set_gren_builtin (Dict.remove key dict)
83 |
84 |
85 | {-| Toggle a value in a set. If the value isn't in the set, it is added. If the
86 | value is in the set, it is removed.
87 | -}
88 | toggle : comparable -> Set comparable -> Set comparable
89 | toggle key (Set_gren_builtin dict) =
90 | when Dict.get key dict is
91 | Just _ ->
92 | Set_gren_builtin <| Dict.remove key dict
93 |
94 | Nothing ->
95 | Set_gren_builtin <| Dict.set key {} dict
96 |
97 |
98 | {-| Determine if a set is empty.
99 | -}
100 | isEmpty : Set a -> Bool
101 | isEmpty (Set_gren_builtin dict) =
102 | Dict.isEmpty dict
103 |
104 |
105 | {-| Determine if a value is in a set.
106 | -}
107 | member : comparable -> Set comparable -> Bool
108 | member key (Set_gren_builtin dict) =
109 | Dict.member key dict
110 |
111 |
112 | {-| Determine the number of elements in a set.
113 | -}
114 | count : Set a -> Int
115 | count (Set_gren_builtin dict) =
116 | Dict.count dict
117 |
118 |
119 | {-| Get the first element of the set.
120 | -}
121 | first : Set a -> Maybe a
122 | first (Set_gren_builtin dict) =
123 | Maybe.map .key (Dict.first dict)
124 |
125 |
126 | {-| Get the last element of the set.
127 | -}
128 | last : Set a -> Maybe a
129 | last (Set_gren_builtin dict) =
130 | Maybe.map .key (Dict.last dict)
131 |
132 |
133 | {-| Find the first value that passes the test.
134 | -}
135 | findFirst : (a -> Bool) -> Set a -> Maybe a
136 | findFirst fn (Set_gren_builtin dict) =
137 | Maybe.map .key (Dict.findFirst (\key _ -> fn key) dict)
138 |
139 |
140 | {-| Find the last value that passes the test.
141 | -}
142 | findLast : (a -> Bool) -> Set a -> Maybe a
143 | findLast fn (Set_gren_builtin dict) =
144 | Maybe.map .key (Dict.findLast (\key _ -> fn key) dict)
145 |
146 |
147 | {-| Checks if any value in the set passes the test.
148 | -}
149 | any : (a -> Bool) -> Set a -> Bool
150 | any fn (Set_gren_builtin dict) =
151 | Dict.any (\key _ -> fn key) dict
152 |
153 |
154 | {-| Checks if all values in the set passes the test.
155 | -}
156 | all : (a -> Bool) -> Set a -> Bool
157 | all fn (Set_gren_builtin dict) =
158 | Dict.all (\key _ -> fn key) dict
159 |
160 |
161 | {-| Get the union of two sets. Keep all values.
162 | -}
163 | union : Set comparable -> Set comparable -> Set comparable
164 | union (Set_gren_builtin dict1) (Set_gren_builtin dict2) =
165 | Set_gren_builtin (Dict.union dict1 dict2)
166 |
167 |
168 | {-| Get the intersection of two sets. Keeps values that appear in both sets.
169 | -}
170 | intersect : Set comparable -> Set comparable -> Set comparable
171 | intersect (Set_gren_builtin dict1) (Set_gren_builtin dict2) =
172 | Set_gren_builtin (Dict.intersect dict1 dict2)
173 |
174 |
175 | {-| Get the difference between the first set and the second. Keeps values
176 | that do not appear in the second set.
177 | -}
178 | diff : Set comparable -> Set comparable -> Set comparable
179 | diff (Set_gren_builtin dict1) (Set_gren_builtin dict2) =
180 | Set_gren_builtin (Dict.diff dict1 dict2)
181 |
182 |
183 | {-| Convert a set into an array, sorted from lowest to highest.
184 | -}
185 | toArray : Set a -> Array a
186 | toArray (Set_gren_builtin dict) =
187 | Dict.keys dict
188 |
189 |
190 | {-| Convert an array into a set, removing any duplicates.
191 | -}
192 | fromArray : Array comparable -> Set comparable
193 | fromArray array =
194 | Array.foldl set empty array
195 |
196 |
197 | {-| Fold over the values in a set, in order from lowest to highest.
198 | -}
199 | foldl : (a -> b -> b) -> b -> Set a -> b
200 | foldl func initialState (Set_gren_builtin dict) =
201 | Dict.foldl (\key _ state -> func key state) initialState dict
202 |
203 |
204 | {-| Fold over the values in a set, in order from highest to lowest.
205 | -}
206 | foldr : (a -> b -> b) -> b -> Set a -> b
207 | foldr func initialState (Set_gren_builtin dict) =
208 | Dict.foldr (\key _ state -> func key state) initialState dict
209 |
210 |
211 | {-| Map a function onto a set, creating a new set with no duplicates.
212 | -}
213 | map : (comparable -> comparable2) -> Set comparable -> Set comparable2
214 | map func coll =
215 | foldl (\x xs -> set (func x) xs) empty coll
216 |
217 |
218 | {-| Only keep elements that pass the given test.
219 |
220 | import Set exposing (Set)
221 |
222 | numbers : Set Int
223 | numbers =
224 | Set.fromArray [ -2, -1, 0, 1, 2 ]
225 |
226 | positives : Set Int
227 | positives =
228 | Set.keepIf (\x -> x > 0) numbers
229 |
230 | -- positives == Set.fromArray [1,2]
231 |
232 | -}
233 | keepIf : (comparable -> Bool) -> Set comparable -> Set comparable
234 | keepIf isGood (Set_gren_builtin dict) =
235 | Set_gren_builtin (Dict.keepIf (\key _ -> isGood key) dict)
236 |
237 |
238 | {-| Remove unwanted results of a map operation.
239 |
240 | import Set
241 |
242 | strings : Set String
243 | strings =
244 | Set.fromArray [ "3", "not a number", "-5" ]
245 |
246 | numbers : Set Int
247 | numbers =
248 | Set.mapAndKeepJust String.toInt strings
249 |
250 | -- numbers == Set.fromArray [ 3, -5 ]
251 | -}
252 | mapAndKeepJust : (comparable -> Maybe comparable2) -> Set comparable -> Set comparable2
253 | mapAndKeepJust toMaybe coll =
254 | foldl
255 | (\old news ->
256 | when toMaybe old is
257 | Just new ->
258 | set new news
259 |
260 | Nothing ->
261 | news
262 | )
263 | empty
264 | coll
265 |
266 |
267 | {-| Create two new sets. The first contains all the elements that passed the
268 | given test, and the second contains all the elements that did not.
269 | -}
270 | partition : (comparable -> Bool) -> Set comparable -> { trues : Set comparable, falses : Set comparable }
271 | partition isGood (Set_gren_builtin dict) =
272 | let
273 | { trues, falses } =
274 | Dict.partition (\key _ -> isGood key) dict
275 | in
276 | { trues = Set_gren_builtin trues
277 | , falses = Set_gren_builtin falses
278 | }
279 |
--------------------------------------------------------------------------------
/src/Stream/Log.gren:
--------------------------------------------------------------------------------
1 | module Stream.Log exposing
2 | ( bytes
3 | , string
4 | , line
5 | )
6 |
7 |
8 | {-| The functions in this module let's you send data to streams on a best effort basis. If
9 | something goes wrong, the error will be silently ignored.
10 |
11 | We don't normally encourage ignoring errors in this way, but for the explicit purpose of logging
12 | it's not always clear how you would handle an error. If you cannot log an error message to the terminal,
13 | do you try again? Try again on another stream? What if that also fails?
14 |
15 | Only use these functions if there is no sane way to handle the event in which sending data to a stream
16 | fails!
17 |
18 | # Operations
19 |
20 | @docs bytes, string, line
21 |
22 | -}
23 |
24 |
25 | import Basics exposing ((|>), (++))
26 | import Bytes exposing (Bytes)
27 | import Stream
28 | import String exposing (String)
29 | import Task exposing (Task)
30 |
31 |
32 | {-| Send `Bytes` to a writable byte stream. Any potential error is ignored.
33 | -}
34 | bytes : Stream.Writable Bytes -> Bytes -> Task x {}
35 | bytes stream data =
36 | Stream.write data stream
37 | |> Task.andThen (\_ -> Task.succeed {})
38 | |> Task.onError (\_ -> Task.succeed {})
39 |
40 |
41 | {-| Send a `String` to a writable byte stream. Any potential error is ignored.
42 | -}
43 | string : Stream.Writable Bytes -> String -> Task x {}
44 | string stream data =
45 | bytes stream (Bytes.fromString data)
46 |
47 |
48 | {-| Send a `String` to a writable byte stream. The ´String` will end with a newline character.
49 | Any potential error is ignored.
50 | -}
51 | line : Stream.Writable Bytes -> String -> Task x {}
52 | line stream data =
53 | string stream (data ++ "\n")
54 |
--------------------------------------------------------------------------------
/src/String/Regex.gren:
--------------------------------------------------------------------------------
1 | module String.Regex exposing
2 | ( Regex
3 | , fromString
4 | , fromStringWith
5 | , Options
6 | , never
7 | , contains
8 | , split
9 | , find
10 | , replace
11 | , Match
12 | , splitAtMost
13 | , findAtMost
14 | , replaceAtMost
15 | )
16 |
17 |
18 | {-| An API for working with regex. The syntax matches the [`RegExp`][js]
19 | library from JavaScript.
20 |
21 | [js]: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
22 |
23 | @docs Regex, fromString, fromStringWith, Options, never
24 |
25 | ## Use
26 | @docs contains, split, find, replace, Match
27 |
28 | ## Fancier Uses
29 | @docs splitAtMost, findAtMost, replaceAtMost
30 |
31 | -}
32 |
33 |
34 | import Array exposing (Array)
35 | import Basics exposing (..)
36 | import Maybe exposing (Maybe(..))
37 | import String exposing (String)
38 | import Gren.Kernel.Regex
39 |
40 |
41 |
42 | -- CREATE
43 |
44 |
45 | {-| A regular expression [as specified in JavaScript][js].
46 |
47 | [js]: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
48 |
49 | -}
50 | type Regex = Regex
51 |
52 |
53 | {-| Try to create a `Regex`. Not all strings are valid though, so you get a
54 | `Maybe' back. This means you can safely accept input from users.
55 |
56 | import String.Regex as Regex
57 |
58 | lowerCase : Regex.Regex
59 | lowerCase =
60 | Maybe.withDefault Regex.never <|
61 | Regex.fromString "[a-z]+"
62 |
63 | **Note:** There are some [shorthand character classes][short] like `\w` for
64 | word characters, `\s` for whitespace characters, and `\d` for digits. **Make
65 | sure they are properly escaped!** If you specify them directly in your code,
66 | they would look like `"\\w\\s\\d"`.
67 |
68 | [short]: https://www.regular-expressions.info/shorthand.html
69 | -}
70 | fromString : String -> Maybe Regex
71 | fromString string =
72 | fromStringWith { caseInsensitive = False, multiline = False } string
73 |
74 |
75 | {-| Create a `Regex` with some additional options. For example, you can define
76 | `fromString` like this:
77 |
78 | import String.Regex as Regex
79 |
80 | fromString : String -> Maybe Regex.Regex
81 | fromString string =
82 | fromStringWith { caseInsensitive = False, multiline = False } string
83 |
84 | -}
85 | fromStringWith : Options -> String -> Maybe Regex
86 | fromStringWith =
87 | Gren.Kernel.Regex.fromStringWith
88 |
89 |
90 | {-|-}
91 | type alias Options =
92 | { caseInsensitive : Bool
93 | , multiline : Bool
94 | }
95 |
96 |
97 | {-| A regular expression that never matches any string.
98 | -}
99 | never : Regex
100 | never =
101 | Gren.Kernel.Regex.never
102 |
103 |
104 |
105 | -- USE
106 |
107 |
108 | {-| Check to see if a Regex is contained in a string.
109 |
110 | import String.Regex as Regex
111 |
112 | digit : Regex.Regex
113 | digit =
114 | Maybe.withDefault Regex.never <|
115 | Regex.fromString "[0-9]"
116 |
117 | -- Regex.contains digit "abc123" == True
118 | -- Regex.contains digit "abcxyz" == False
119 | -}
120 | contains : Regex -> String -> Bool
121 | contains =
122 | Gren.Kernel.Regex.contains
123 |
124 |
125 | {-| Split a string. The following example will split on commas and tolerate
126 | whitespace on either side of the comma:
127 |
128 | import String.Regex as Regex
129 |
130 | comma : Regex.Regex
131 | comma =
132 | Maybe.withDefault Regex.never <|
133 | Regex.fromString " *, *"
134 |
135 | -- Regex.split comma "tom,99,90,85" == ["tom","99","90","85"]
136 | -- Regex.split comma "tom, 99, 90, 85" == ["tom","99","90","85"]
137 | -- Regex.split comma "tom , 99, 90, 85" == ["tom","99","90","85"]
138 |
139 | If you want some really fancy splits, a library like
140 | [`gren-lang/parser`][parser] will probably be easier to use.
141 |
142 | [parser]: /packages/gren-lang/parser/latest
143 | -}
144 | split : Regex -> String -> Array String
145 | split =
146 | Gren.Kernel.Regex.splitAtMost Gren.Kernel.Regex.infinity
147 |
148 |
149 | {-| Find matches in a string:
150 |
151 | import String.Regex as Regex
152 |
153 | location : Regex.Regex
154 | location =
155 | Maybe.withDefault Regex.never <|
156 | Regex.fromString "[oi]n a (\\w+)"
157 |
158 | places : Array Regex.Match
159 | places =
160 | Regex.find location "I am on a boat in a lake."
161 |
162 | -- map .match places == [ "on a boat", "in a lake" ]
163 | -- map .submatches places == [ [Just "boat"], [Just "lake"] ]
164 |
165 | If you need `submatches` for some reason, a library like
166 | [`gren-lang/parser`][parser] will probably lead to better code in the long run.
167 |
168 | [parser]: /packages/gren-lang/parser/latest
169 | -}
170 | find : Regex -> String -> Array Match
171 | find =
172 | Gren.Kernel.Regex.findAtMost Gren.Kernel.Regex.infinity
173 |
174 |
175 | {-| The details about a particular match:
176 |
177 | * `match` — the full string of the match.
178 | * `index` — the index of the match in the original string.
179 | * `number` — if you find many matches, you can think of each one
180 | as being labeled with a `number` starting at one. So the first time you
181 | find a match, that is match `number` one. Second time is match `number` two.
182 | This is useful when paired with `replace` if replacement is dependent on how
183 | many times a pattern has appeared before.
184 | * `submatches` — a `Regex` can have [subpatterns][sub], sup-parts that
185 | are in parentheses. This is a array of all these submatches. This is kind of
186 | garbage to use, and using a package like [`gren-lang/parser`][parser] is
187 | probably easier.
188 |
189 | [sub]: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Parenthesized_Substring_Matches
190 | [parser]: /packages/gren-lang/parser/latest
191 |
192 | -}
193 | type alias Match =
194 | { match : String
195 | , index : Int
196 | , number : Int
197 | , submatches : Array (Maybe String)
198 | }
199 |
200 |
201 | {-| Replace matches. The function from `Match` to `String` lets
202 | you use the details of a specific match when making replacements.
203 |
204 | import String.Regex as Regex
205 |
206 | userReplace : String -> (Regex.Match -> String) -> String -> String
207 | userReplace userRegex replacer string =
208 | case Regex.fromString userRegex of
209 | Nothing ->
210 | string
211 |
212 | Just regex ->
213 | Regex.replace regex replacer string
214 |
215 | devowel : String -> String
216 | devowel string =
217 | userReplace "[aeiou]" (\_ -> "") string
218 |
219 | -- devowel "The quick brown fox" == "Th qck brwn fx"
220 |
221 | reverseWords : String -> String
222 | reverseWords string =
223 | userReplace "\\w+" (.match >> String.reverse) string
224 |
225 | -- reverseWords "deliver mined parts" == "reviled denim strap"
226 | -}
227 | replace : Regex -> (Match -> String) -> String -> String
228 | replace =
229 | Gren.Kernel.Regex.replaceAtMost Gren.Kernel.Regex.infinity
230 |
231 |
232 |
233 | -- AT MOST
234 |
235 |
236 | {-| Just like `split` but it stops after some number of matches.
237 |
238 | A library like [`gren-lang/parser`][parser] will probably lead to better code in
239 | the long run.
240 |
241 | [parser]: /packages/gren-lang/parser/latest
242 | -}
243 | splitAtMost : Int -> Regex -> String -> Array String
244 | splitAtMost =
245 | Gren.Kernel.Regex.splitAtMost
246 |
247 |
248 | {-| Just like `find` but it stops after some number of matches.
249 |
250 | A library like [`gren-lang/parser`][parser] will probably lead to better code in
251 | the long run.
252 |
253 | [parser]: /packages/gren-lang/parser/latest
254 | -}
255 | findAtMost : Int -> Regex -> String -> Array Match
256 | findAtMost =
257 | Gren.Kernel.Regex.findAtMost
258 |
259 |
260 | {-| Just like `replace` but it stops after some number of matches.
261 |
262 | A library like [`gren-lang/parser`][parser] will probably lead to better code in
263 | the long run.
264 |
265 | [parser]: /packages/gren-lang/parser/latest
266 | -}
267 | replaceAtMost : Int -> Regex -> (Match -> String) -> String -> String
268 | replaceAtMost =
269 | Gren.Kernel.Regex.replaceAtMost
270 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | app
--------------------------------------------------------------------------------
/tests/gren.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "application",
3 | "platform": "node",
4 | "source-directories": [
5 | "src"
6 | ],
7 | "gren-version": "0.5.3",
8 | "dependencies": {
9 | "direct": {
10 | "gren-lang/core": "local:../",
11 | "gren-lang/node": "5.0.0",
12 | "gren-lang/test": "4.1.0",
13 | "gren-lang/test-runner-node": "6.0.0"
14 | },
15 | "indirect": {
16 | "gren-lang/url": "5.0.0"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/run-tests.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | gren make src/Main.gren
4 | node app
5 |
--------------------------------------------------------------------------------
/tests/src/Main.gren:
--------------------------------------------------------------------------------
1 | module Main exposing (main)
2 |
3 | import Basics exposing (..)
4 | import Json.Decode exposing (Value)
5 | import Node
6 | import Platform.Cmd exposing (Cmd)
7 | import Task exposing (..)
8 | import Test exposing (..)
9 | import Test.Array as Array
10 | import Test.ArrayBuilder as ArrayBuilder
11 | import Test.Basics as Basics
12 | import Test.Bitwise as Bitwise
13 | import Test.Bytes as Bytes
14 | import Test.Char as Char
15 | import Test.CodeGen as CodeGen
16 | import Test.Dict as Dict
17 | import Test.Equality as Equality
18 | import Test.Json as Json
19 | import Test.Math as Math
20 | import Test.Maybe as Maybe
21 | import Test.Result as Result
22 | import Test.Runner.Node exposing (Program, run)
23 | import Test.Set as Set
24 | import Test.String as String
25 | import Test.Regex as Regex
26 |
27 |
28 | main : Program
29 | main =
30 | run <|
31 | describe "Gren Core Library Tests"
32 | [ Array.tests
33 | , ArrayBuilder.tests
34 | , Basics.tests
35 | , Bitwise.tests
36 | , Bytes.tests
37 | , Char.tests
38 | , CodeGen.tests
39 | , Dict.tests
40 | , Equality.tests
41 | , Json.tests
42 | , Result.tests
43 | , Set.tests
44 | , String.tests
45 | , Maybe.tests
46 | , Math.tests
47 | , Regex.tests
48 | ]
49 |
--------------------------------------------------------------------------------
/tests/src/Test/ArrayBuilder.gren:
--------------------------------------------------------------------------------
1 | module Test.ArrayBuilder exposing (tests)
2 |
3 | import Array
4 | import Array.Builder as Builder
5 | import Basics exposing (..)
6 | import Expect
7 | import Test exposing (..)
8 | import Fuzz
9 |
10 |
11 |
12 | tests : Test
13 | tests =
14 | describe "Array.Builder Tests"
15 | [ fuzz Fuzz.int "An empty builder makes for an empty array, regardless of capacity" <| \capacity ->
16 | Builder.empty capacity
17 | |> Builder.toArray
18 | |> Expect.equalArrays []
19 | , test "Can build an array using builder API" <| \{} ->
20 | Builder.empty 3
21 | |> Builder.pushLast 1
22 | |> Builder.pushLast 2
23 | |> Builder.pushLast 3
24 | |> Builder.toArray
25 | |> Expect.equalArrays [ 1, 2, 3 ]
26 | , test "Capacity doesn't limit what can be inserted into the builder" <| \{} ->
27 | let
28 | correctCapacity =
29 | Builder.empty 2
30 | |> Builder.pushLast 1
31 | |> Builder.pushLast 2
32 | |> Builder.toArray
33 |
34 | wrongCapacity =
35 | Builder.empty 1
36 | |> Builder.pushLast 1
37 | |> Builder.pushLast 2
38 | |> Builder.toArray
39 | in
40 | Expect.equalArrays correctCapacity wrongCapacity
41 | , fuzz (Fuzz.array Fuzz.int) "Builder API is just a optimization, it's the same as using Array.pushLast" <| \array ->
42 | Array.foldl (\val builder -> Builder.pushLast val builder) (Builder.empty 0) array
43 | |> Builder.toArray
44 | |> Expect.equalArrays array
45 | , test "Cannot detect mutation" <| \{} ->
46 | let
47 | empty =
48 | Builder.empty 0
49 |
50 | first =
51 | Builder.pushLast "first" empty
52 |
53 | second =
54 | Builder.pushLast "second" empty
55 | in
56 | Expect.all
57 | [ \{} -> Expect.equalArrays [] (Builder.toArray empty)
58 | , \{} -> Expect.equalArrays [ "first" ] (Builder.toArray first)
59 | , \{} -> Expect.equalArrays [ "second" ] (Builder.toArray second)
60 | ]
61 | {}
62 | , test "Cannot detect mutation after toArray" <| \{} ->
63 | let
64 | empty =
65 | Builder.empty 0
66 |
67 | first =
68 | Builder.pushLast "first" empty
69 |
70 | firstArray =
71 | Builder.toArray first
72 |
73 | secondArray =
74 | first
75 | |> Builder.pushLast "second"
76 | |> Builder.toArray
77 | in
78 | Expect.all
79 | [ \{} -> Expect.equalArrays [] (Builder.toArray empty)
80 | , \{} -> Expect.equalArrays [ "first" ] firstArray
81 | , \{} -> Expect.equalArrays [ "first", "second" ] secondArray
82 | ]
83 | {}
84 | , test "Cannot detect mutation with equality" <| \{} ->
85 | let
86 | empty =
87 | Builder.empty 0
88 |
89 | first =
90 | empty
91 | |> Builder.pushLast 1
92 | in
93 | Expect.equal empty (Builder.empty 0)
94 | , test "Builder.append" <| \{} ->
95 | Builder.empty 0
96 | |> Builder.pushLast 5
97 | |> Builder.pushLast 4
98 | |> Builder.append [ 3, 2, 1 ]
99 | |> Builder.toArray
100 | |> Expect.equalArrays [ 5, 4, 3, 2, 1 ]
101 | , test "Cannot detect mutation with append" <| \{} ->
102 | let
103 | init =
104 | Builder.empty 0
105 |
106 | first =
107 | Builder.append [ 1, 2 ] init
108 |
109 | second =
110 | Builder.pushLast 3 init
111 | in
112 | Expect.all
113 | [ \{} -> Expect.equalArrays [] (Builder.toArray init)
114 | , \{} -> Expect.equalArrays [ 1, 2 ] (Builder.toArray first)
115 | , \{} -> Expect.equalArrays [ 3 ] (Builder.toArray second)
116 | ]
117 | {}
118 | , test "Builder.fromArray" <| \{} ->
119 | let
120 | array =
121 | [ 1, 2, 3 ]
122 |
123 | built =
124 | Builder.fromArray array
125 | |> Builder.pushLast 4
126 | |> Builder.pushLast 5
127 | |> Builder.toArray
128 | in
129 | Expect.all
130 | [ \{} -> Expect.equalArrays [ 1, 2, 3 ] array
131 | , \{} -> Expect.equalArrays [ 1, 2, 3, 4, 5 ] built
132 | ]
133 | {}
134 | ]
135 |
--------------------------------------------------------------------------------
/tests/src/Test/Basics.gren:
--------------------------------------------------------------------------------
1 | module Test.Basics exposing (tests)
2 |
3 | import Array
4 | import Basics exposing (..)
5 | import Debug exposing (toString)
6 | import Dict
7 | import Math
8 | import Expect exposing (FloatingPointTolerance(..))
9 | import Array
10 | import Set
11 | import String
12 | import Test exposing (..)
13 |
14 |
15 | tests : Test
16 | tests =
17 | let
18 | comparison =
19 | describe "Comparison"
20 | [ test "max" <| \{} -> Expect.equal 42 (max 32 42)
21 | , test "min" <| \{} -> Expect.equal 42 (min 91 42)
22 | , test "clamp low" <| \{} -> Expect.equal 10 (clamp 10 20 5)
23 | , test "clamp mid" <| \{} -> Expect.equal 15 (clamp 10 20 15)
24 | , test "clamp high" <| \{} -> Expect.equal 20 (clamp 10 20 25)
25 | , test "5 < 6" <| \{} -> Expect.equal True (5 < 6)
26 | , test "6 < 5" <| \{} -> Expect.equal False (6 < 5)
27 | , test "6 < 6" <| \{} -> Expect.equal False (6 < 6)
28 | , test "5 > 6" <| \{} -> Expect.equal False (5 > 6)
29 | , test "6 > 5" <| \{} -> Expect.equal True (6 > 5)
30 | , test "6 > 6" <| \{} -> Expect.equal False (6 > 6)
31 | , test "5 <= 6" <| \{} -> Expect.equal True (5 <= 6)
32 | , test "6 <= 5" <| \{} -> Expect.equal False (6 <= 5)
33 | , test "6 <= 6" <| \{} -> Expect.equal True (6 <= 6)
34 | , test "compare \"A\" \"B\"" <| \{} -> Expect.equal LT (compare "A" "B")
35 | , test "compare 'f' 'f'" <| \{} -> Expect.equal EQ (compare 'f' 'f')
36 | , test "compare ['a'] ['b']" <| \{} -> Expect.equal LT (compare [ 'a' ] [ 'b' ])
37 | , test "set equality" <| \{} -> Expect.equal (Set.fromArray [ 1, 2 ]) (Set.fromArray [ 2, 1 ])
38 | , test "dict equality" <| \{} -> Expect.equal (Dict.empty |> Dict.set 1 1 |> Dict.set 2 2) (Dict.empty |> Dict.set 2 2 |> Dict.set 1 1)
39 | , test "char equality" <| \{} -> Expect.notEqual '0' '饑'
40 | ]
41 |
42 | toStringTests =
43 | describe "toString Tests"
44 | [ test "toString Int" <| \{} -> Expect.equal "42" (toString 42)
45 | , test "toString Float" <| \{} -> Expect.equal "42.52" (toString 42.52)
46 | , test "toString Char" <| \{} -> Expect.equal "'c'" (toString 'c')
47 | , test "toString Char single quote" <| \{} -> Expect.equal "'\\''" (toString '\'')
48 | , test "toString Char double quote" <| \{} -> Expect.equal "'\"'" (toString '"')
49 | , test "toString String single quote" <| \{} -> Expect.equal "\"not 'escaped'\"" (toString "not 'escaped'")
50 | , test "toString String double quote" <| \{} -> Expect.equal "\"are \\\"escaped\\\"\"" (toString "are \"escaped\"")
51 | , test "toString record" <| \{} -> Expect.equal "{ field = [0] }" (toString { field = [ 0 ] })
52 | ]
53 |
54 | booleanTests =
55 | describe "Boolean Tests"
56 | [ test "False && False" <| \{} -> Expect.equal False (False && False)
57 | , test "False && True" <| \{} -> Expect.equal False (False && True)
58 | , test "True && False" <| \{} -> Expect.equal False (True && False)
59 | , test "True && True" <| \{} -> Expect.equal True (True && True)
60 | , test "False || False" <| \{} -> Expect.equal False (False || False)
61 | , test "False || True" <| \{} -> Expect.equal True (False || True)
62 | , test "True || False" <| \{} -> Expect.equal True (True || False)
63 | , test "True || True" <| \{} -> Expect.equal True (True || True)
64 | , test "xor False False" <| \{} -> Expect.equal False (xor False False)
65 | , test "xor False True" <| \{} -> Expect.equal True (xor False True)
66 | , test "xor True False" <| \{} -> Expect.equal True (xor True False)
67 | , test "xor True True" <| \{} -> Expect.equal False (xor True True)
68 | , test "not True" <| \{} -> Expect.equal False (not True)
69 | , test "not False" <| \{} -> Expect.equal True (not False)
70 | ]
71 |
72 |
73 | miscTests =
74 | describe "Miscellaneous Tests"
75 | [ test "isNaN (0/0)" <| \{} -> Expect.equal True (isNaN (0 / 0))
76 | , test "isNaN (sqrt -1)" <| \{} -> Expect.equal True (isNaN (Math.sqrt -1))
77 | , test "isNaN (1/0)" <| \{} -> Expect.equal False (isNaN (1 / 0))
78 | , test "isNaN 1" <| \{} -> Expect.equal False (isNaN 1)
79 | , test "isInfinite (0/0)" <| \{} -> Expect.equal False (isInfinite (0 / 0))
80 | , test "isInfinite (sqrt -1)" <| \{} -> Expect.equal False (isInfinite (Math.sqrt -1))
81 | , test "isInfinite (1/0)" <| \{} -> Expect.equal True (isInfinite (1 / 0))
82 | , test "isInfinite 1" <| \{} -> Expect.equal False (isInfinite 1)
83 | , test "\"hello\" ++ \"world\"" <| \{} -> Expect.equal "helloworld" ("hello" ++ "world")
84 | , test "[1, 1, 2] ++ [3, 5, 8]" <| \{} -> Expect.equal [ 1, 1, 2, 3, 5, 8 ] ([ 1, 1, 2 ] ++ [ 3, 5, 8 ])
85 | ]
86 |
87 | higherOrderTests =
88 | describe "Higher Order Helpers"
89 | [ test "identity 'c'" <| \{} -> Expect.equal 'c' (identity 'c')
90 | , test "<|" <| \{} -> Expect.equal 9 (identity <| 3 + 6)
91 | , test "|>" <| \{} -> Expect.equal 9 (3 + 6 |> identity)
92 | , test "<<" <| \{} -> Expect.equal True (not << xor True <| True)
93 | , describe ">>"
94 | [ test "with xor" <|
95 | \{} ->
96 | (True |> xor True >> not)
97 | |> Expect.equal True
98 | , test "with a record accessor" <|
99 | \{} ->
100 | [ { foo = "NaS", bar = "baz" } ]
101 | |> Array.map (.foo >> String.reverse)
102 | |> Expect.equal [ "SaN" ]
103 | ]
104 | ]
105 | in
106 | describe "Basics"
107 | [ comparison
108 | , toStringTests
109 | , booleanTests
110 | , miscTests
111 | , higherOrderTests
112 | ]
113 |
--------------------------------------------------------------------------------
/tests/src/Test/Bitwise.gren:
--------------------------------------------------------------------------------
1 | module Test.Bitwise exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Bitwise
5 | import Expect
6 | import Test exposing (..)
7 |
8 |
9 | tests : Test
10 | tests =
11 | describe "Bitwise"
12 | [ describe "and"
13 | [ test "and with 32 bit integers" <| \{} -> Expect.equal 1 (Bitwise.and 5 3)
14 | , test "and with 0 as first argument" <| \{} -> Expect.equal 0 (Bitwise.and 0 1450)
15 | , test "and with 0 as second argument" <| \{} -> Expect.equal 0 (Bitwise.and 274 0)
16 | , test "and with -1 as first argument" <| \{} -> Expect.equal 2671 (Bitwise.and -1 2671)
17 | , test "and with -1 as second argument" <| \{} -> Expect.equal 96 (Bitwise.and 96 -1)
18 | ]
19 | , describe "or"
20 | [ test "or with 32 bit integers" <| \{} -> Expect.equal 15 (Bitwise.or 9 14)
21 | , test "or with 0 as first argument" <| \{} -> Expect.equal 843 (Bitwise.or 0 843)
22 | , test "or with 0 as second argument" <| \{} -> Expect.equal 19 (Bitwise.or 19 0)
23 | , test "or with -1 as first argument" <| \{} -> Expect.equal -1 (Bitwise.or -1 2360)
24 | , test "or with -1 as second argument" <| \{} -> Expect.equal -1 (Bitwise.or 3 -1)
25 | ]
26 | , describe "xor"
27 | [ test "xor with 32 bit integers" <| \{} -> Expect.equal 604 (Bitwise.xor 580 24)
28 | , test "xor with 0 as first argument" <| \{} -> Expect.equal 56 (Bitwise.xor 0 56)
29 | , test "xor with 0 as second argument" <| \{} -> Expect.equal -268 (Bitwise.xor -268 0)
30 | , test "xor with -1 as first argument" <| \{} -> Expect.equal -25 (Bitwise.xor -1 24)
31 | , test "xor with -1 as second argument" <| \{} -> Expect.equal 25601 (Bitwise.xor -25602 -1)
32 | ]
33 | , describe "complement"
34 | [ test "complement a positive" <| \{} -> Expect.equal -9 (Bitwise.complement 8)
35 | , test "complement a negative" <| \{} -> Expect.equal 278 (Bitwise.complement -279)
36 | ]
37 | , describe "countLeadingZeros"
38 | [ test "0" <| \{} -> Expect.equal 32 (Bitwise.countLeadingZeros 0)
39 | , test "1" <| \{} -> Expect.equal 31 (Bitwise.countLeadingZeros 1)
40 | , test "0xFFFF" <| \{} -> Expect.equal 16 (Bitwise.countLeadingZeros 0xFFFF)
41 | ]
42 | , describe "shiftLeftBy"
43 | [ test "8 |> shiftLeftBy 1 == 16" <| \{} -> Expect.equal 16 (8 |> Bitwise.shiftLeftBy 1)
44 | , test "8 |> shiftLeftby 2 == 32" <| \{} -> Expect.equal 32 (8 |> Bitwise.shiftLeftBy 2)
45 | ]
46 | , describe "shiftRightBy"
47 | [ test "32 |> shiftRight 1 == 16" <| \{} -> Expect.equal 16 (32 |> Bitwise.shiftRightBy 1)
48 | , test "32 |> shiftRight 2 == 8" <| \{} -> Expect.equal 8 (32 |> Bitwise.shiftRightBy 2)
49 | , test "-32 |> shiftRight 1 == -16" <| \{} -> Expect.equal -16 (-32 |> Bitwise.shiftRightBy 1)
50 | ]
51 | , describe "shiftRightZfBy"
52 | [ test "32 |> shiftRightZfBy 1 == 16" <| \{} -> Expect.equal 16 (32 |> Bitwise.shiftRightZfBy 1)
53 | , test "32 |> shiftRightZfBy 2 == 8" <| \{} -> Expect.equal 8 (32 |> Bitwise.shiftRightZfBy 2)
54 | , test "-32 |> shiftRightZfBy 1 == 2147483632" <| \{} -> Expect.equal 2147483632 (-32 |> Bitwise.shiftRightZfBy 1)
55 | ]
56 | ]
57 |
--------------------------------------------------------------------------------
/tests/src/Test/Bytes.gren:
--------------------------------------------------------------------------------
1 | module Test.Bytes exposing (tests)
2 |
3 | import Bytes exposing (Bytes)
4 | import Bytes.Encode
5 | import Expect
6 | import Fuzz
7 | import Test exposing (..)
8 |
9 |
10 | encodeInt : Int -> Bytes
11 | encodeInt i =
12 | Bytes.Encode.encode <|
13 | Bytes.Encode.signedInt8 i
14 |
15 |
16 | tests : Test
17 | tests =
18 | describe "Byte comparison"
19 | [ fuzz Fuzz.string "same strings are equal" <| \s ->
20 | Expect.equal (Bytes.fromString s) (Bytes.fromString s)
21 | , fuzz (Fuzz.intRange -128 127) "same ints are equal" <| \i ->
22 | Expect.equal (encodeInt i) (encodeInt i)
23 | , fuzz Fuzz.string "different strings are not equal" <| \s ->
24 | Expect.notEqual (Bytes.fromString s) (Bytes.fromString (s ++ "a"))
25 | , fuzz (Fuzz.intRange -100 100) "different ints are not equal" <| \i ->
26 | Expect.notEqual (encodeInt i) (encodeInt (i + 1))
27 | ]
28 |
--------------------------------------------------------------------------------
/tests/src/Test/Char.gren:
--------------------------------------------------------------------------------
1 | module Test.Char exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Char exposing (..)
5 | import Expect
6 | import Array
7 | import Test exposing (..)
8 |
9 |
10 | lower =
11 | [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ]
12 |
13 |
14 | upper =
15 | [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]
16 |
17 |
18 | dec =
19 | [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]
20 |
21 |
22 | oct =
23 | Array.takeFirst 8 dec
24 |
25 |
26 | hexLower =
27 | Array.takeFirst 6 lower
28 |
29 |
30 | hexUpper =
31 | Array.takeFirst 6 upper
32 |
33 |
34 | hex =
35 | Array.prepend hexLower hexUpper |> Array.prepend dec
36 |
37 |
38 | lowerCodes =
39 | Array.range 97 (97 + Array.length lower - 1)
40 |
41 |
42 | upperCodes =
43 | Array.range 65 (65 + Array.length upper - 1)
44 |
45 |
46 | decCodes =
47 | Array.range 48 (48 + Array.length dec - 1)
48 |
49 |
50 | oneOf : Array a -> a -> Bool
51 | oneOf array e =
52 | Array.member e array
53 |
54 |
55 | tests : Test
56 | tests =
57 | describe "Char"
58 | [ describe "toCode"
59 | [ test "a-z" <| \{} -> Expect.equal lowerCodes (Array.map toCode lower)
60 | , test "A-Z" <| \{} -> Expect.equal upperCodes (Array.map toCode upper)
61 | , test "0-9" <| \{} -> Expect.equal decCodes (Array.map toCode dec)
62 | , test "UTF-16" <| \{} -> Expect.equal 0x0001D306 (Char.toCode '𝌆')
63 | ]
64 | , describe "fromCode"
65 | [ test "a-z" <| \{} -> Expect.equal lower (Array.map fromCode lowerCodes)
66 | , test "A-Z" <| \{} -> Expect.equal upper (Array.map fromCode upperCodes)
67 | , test "0-9" <| \{} -> Expect.equal dec (Array.map fromCode decCodes)
68 | , test "UTF-16" <| \{} -> Expect.equal '𝌆' (Char.fromCode 0x0001D306)
69 | ]
70 | , describe "isLower"
71 | [ test "a-z" <| \{} -> Expect.equal True (Array.all isLower lower)
72 | , test "A-Z" <| \{} -> Expect.equal False (Array.any isLower upper)
73 | , test "0-9" <| \{} -> Expect.equal False (Array.any isLower dec)
74 | ]
75 | , describe "isUpper"
76 | [ test "a-z" <| \{} -> Expect.equal False (Array.any isUpper lower)
77 | , test "A-Z" <| \{} -> Expect.equal True (Array.all isUpper upper)
78 | , test "0-9" <| \{} -> Expect.equal False (Array.any isUpper dec)
79 | ]
80 | , describe "isDigit"
81 | [ test "a-z" <| \{} -> Expect.equal False (Array.any isDigit lower)
82 | , test "A-Z" <| \{} -> Expect.equal False (Array.any isDigit upper)
83 | , test "0-9" <| \{} -> Expect.equal True (Array.all isDigit dec)
84 | ]
85 | , describe "isHexDigit"
86 | [ test "a-z" <| \{} -> Expect.equal (Array.map (oneOf hex) lower) (Array.map isHexDigit lower)
87 | , test "A-Z" <| \{} -> Expect.equal (Array.map (oneOf hex) upper) (Array.map isHexDigit upper)
88 | , test "0-9" <| \{} -> Expect.equal True (Array.all isHexDigit dec)
89 | ]
90 | , describe "isOctDigit"
91 | [ test "a-z" <| \{} -> Expect.equal False (Array.any isOctDigit lower)
92 | , test "A-Z" <| \{} -> Expect.equal False (Array.any isOctDigit upper)
93 | , test "0-9" <| \{} -> Expect.equal (Array.map (oneOf oct) dec) (Array.map isOctDigit dec)
94 | ]
95 | ]
96 |
--------------------------------------------------------------------------------
/tests/src/Test/CodeGen.gren:
--------------------------------------------------------------------------------
1 | module Test.CodeGen exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Expect
5 | import Maybe exposing (..)
6 | import Test exposing (..)
7 |
8 |
9 | type Wrapper a
10 | = Wrapper a
11 |
12 |
13 | caseUnderscore : Maybe number -> number
14 | caseUnderscore m_ =
15 | when m_ is
16 | Just x ->
17 | x
18 |
19 | Nothing ->
20 | 0
21 |
22 |
23 | patternUnderscore : number
24 | patternUnderscore =
25 | when Just 42 is
26 | Just x_ ->
27 | x_
28 |
29 | Nothing ->
30 | 0
31 |
32 |
33 | letQualified : number
34 | letQualified =
35 | let
36 | (Wrapper x) =
37 | Wrapper 42
38 | in
39 | x
40 |
41 |
42 | caseQualified : number
43 | caseQualified =
44 | when Just 42 is
45 | Maybe.Just x ->
46 | x
47 |
48 | Nothing ->
49 | 0
50 |
51 |
52 | tests : Test
53 | tests =
54 | let
55 | -- We don't strictly speaking need annotations in this let-expression,
56 | -- but having these here exercises the parser to avoid regressions like
57 | -- https://github.com/gren-lang/gren-compiler/issues/1535
58 | underscores : Test
59 | underscores =
60 | describe "Underscores"
61 | [ test "case" <| \{} -> Expect.equal 42 (caseUnderscore (Just 42))
62 | , test "pattern" <| \{} -> Expect.equal 42 patternUnderscore
63 | ]
64 |
65 | qualifiedPatterns : Test
66 | qualifiedPatterns =
67 | describe "Qualified Patterns"
68 | [ test "let" <| \{} -> Expect.equal 42 letQualified
69 | , test "case" <| \{} -> Expect.equal 42 caseQualified
70 | ]
71 |
72 | hex : Test
73 | hex =
74 | describe "Hex"
75 | [ test "0xFFFFFFFF" <|
76 | \{} ->
77 | 0xFFFFFFFF
78 | |> Expect.equal 4294967295
79 | , test "0xD066F00D" <|
80 | \{} ->
81 | 0xD066F00D
82 | |> Expect.equal 3496407053
83 | , test "0x00" <|
84 | \{} ->
85 | 0x00
86 | |> Expect.equal 0
87 | ]
88 | in
89 | describe "CodeGen"
90 | [ underscores
91 | , qualifiedPatterns
92 | , hex
93 | ]
94 |
--------------------------------------------------------------------------------
/tests/src/Test/Dict.gren:
--------------------------------------------------------------------------------
1 | module Test.Dict exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Dict
5 | import Expect
6 | import Array
7 | import Maybe exposing (..)
8 | import Test exposing (..)
9 |
10 |
11 | animals : Dict.Dict String String
12 | animals =
13 | Dict.empty
14 | |> Dict.set "Tom" "cat"
15 | |> Dict.set "Jerry" "mouse"
16 |
17 |
18 | mice : Dict.Dict String String
19 | mice =
20 | Dict.empty
21 | |> Dict.set "Jerry" "mouse"
22 | |> Dict.set "John" "mouse"
23 | |> Dict.set "Mike" "mouse"
24 |
25 |
26 | tests : Test
27 | tests =
28 | let
29 | buildTests =
30 | describe "build Tests"
31 | [ test "singleton" <| \{} -> Expect.equal (Dict.set "k" "v" Dict.empty) (Dict.singleton "k" "v")
32 | , test "set replace" <| \{} -> Expect.equal (Dict.set "k" "vv" Dict.empty) (Dict.set "k" "vv" (Dict.singleton "k" "v"))
33 | , test "update" <| \{} -> Expect.equal (Dict.set "k" "vv" Dict.empty) (Dict.update "k" (\v -> Just "vv") (Dict.singleton "k" "v"))
34 | , test "update Nothing" <| \{} -> Expect.equal Dict.empty (Dict.update "k" (\v -> Nothing) (Dict.singleton "k" "v"))
35 | , test "updateWithDefault" <| \{} ->
36 | Expect.equal
37 | (Dict.set "k" [ "v" ] Dict.empty)
38 | (Dict.updateWithDefault "k" [] (\vs -> Array.pushLast "v" vs) Dict.empty)
39 | , test "updateWithDefault Nothing" <| \{} ->
40 | Expect.equal
41 | (Dict.set "k" [ "v", "v" ] Dict.empty)
42 | (Dict.updateWithDefault "k" [] (\vs -> Array.pushLast "v" vs) (Dict.singleton "k" ["v"]))
43 | , test "remove" <| \{} -> Expect.equal Dict.empty (Dict.remove "k" (Dict.singleton "k" "v"))
44 | , test "remove not found" <| \{} -> Expect.equal (Dict.singleton "k" "v") (Dict.remove "kk" (Dict.singleton "k" "v"))
45 | ]
46 |
47 | queryTests =
48 | describe "query Tests"
49 | [ test "member 1" <| \{} -> Expect.equal True (Dict.member "Tom" animals)
50 | , test "member 2" <| \{} -> Expect.equal False (Dict.member "Spike" animals)
51 | , test "get 1" <| \{} -> Expect.equal (Just "cat") (Dict.get "Tom" animals)
52 | , test "get 2" <| \{} -> Expect.equal Nothing (Dict.get "Spike" animals)
53 | , test "count of empty dictionary" <| \{} -> Expect.equal 0 (Dict.count Dict.empty)
54 | , test "count of example dictionary" <| \{} -> Expect.equal 2 (Dict.count animals)
55 | , test "first value of animals is (J)erry" <| \{} -> Expect.equal (Just { key = "Jerry", value = "mouse" }) (Dict.first animals)
56 | , test "first value of animals is (T)om" <| \{} -> Expect.equal (Just { key = "Tom", value = "cat" }) (Dict.last animals)
57 | , test "there is no cats in mice" <| \{} -> Expect.equal Nothing (Dict.findFirst (\_key val -> val == "cat") mice)
58 | , test "first mouse of mice is Jerry" <| \{} -> Expect.equal (Just { key = "Jerry", value = "mouse" }) (Dict.findFirst (\_key val -> val == "mouse") mice)
59 | , test "last mouse of mice is Mike" <| \{} -> Expect.equal (Just { key = "Mike", value = "mouse" }) (Dict.findLast (\_key val -> val == "mouse") mice)
60 | , test "there is a mouse in animals" <| \{} -> Expect.equal True (Dict.any (\_key value -> value == "mouse") animals)
61 | , test "not all animals in animals are mice" <| \{} -> Expect.equal False (Dict.all (\_key value -> value == "mouse") animals)
62 | , test "all animals in mice are mice, though" <| \{} -> Expect.equal True (Dict.all (\_key value -> value == "mouse") mice)
63 | ]
64 |
65 | combineTests =
66 | describe "combine Tests"
67 | [ test "union" <| \{} -> Expect.equal animals (Dict.union (Dict.singleton "Jerry" "mouse") (Dict.singleton "Tom" "cat"))
68 | , test "union collison" <| \{} -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.union (Dict.singleton "Tom" "cat") (Dict.singleton "Tom" "mouse"))
69 | , test "intersect" <| \{} -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.intersect animals (Dict.singleton "Tom" "cat"))
70 | , test "diff" <| \{} -> Expect.equal (Dict.singleton "Jerry" "mouse") (Dict.diff animals (Dict.singleton "Tom" "cat"))
71 | ]
72 |
73 | transformTests =
74 | describe "transform Tests"
75 | [ test "keepIf" <| \{} -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.keepIf (\k v -> k == "Tom") animals)
76 | , test "map" <| \{} -> Expect.equal (Dict.empty |> Dict.set "Tom" "tac" |> Dict.set "Jerry" "esuom") (Dict.map (\_ v -> String.reverse v) animals)
77 | , test "mapAndKeepJust" <| \{} -> Expect.equal (Dict.singleton "Tom" "tac") (Dict.mapAndKeepJust (\k v -> if k == "Tom" then Just (String.reverse v) else Nothing) animals)
78 | , test "partition" <| \{} -> Expect.equal { trues = Dict.singleton "Tom" "cat", falses = Dict.singleton "Jerry" "mouse" } (Dict.partition (\k v -> k == "Tom") animals)
79 | ]
80 |
81 | mergeTests =
82 | let
83 | setBoth key leftVal rightVal dict =
84 | Dict.set key (leftVal ++ rightVal) dict
85 |
86 | s1 =
87 | Dict.singleton "u1" [ 1 ]
88 |
89 | s2 =
90 | Dict.singleton "u2" [ 2 ]
91 |
92 | s23 =
93 | Dict.singleton "u2" [ 3 ]
94 |
95 | b1 =
96 | Array.foldl (\i dict -> Dict.set i [ i ] dict) Dict.empty (Array.range 1 10)
97 |
98 | b2 =
99 | Array.foldl (\i dict -> Dict.set i [ i ] dict) Dict.empty (Array.range 5 15)
100 |
101 | bExpected =
102 | Dict.empty
103 | |> Dict.set 1 [1]
104 | |> Dict.set 2 [2]
105 | |> Dict.set 3 [3]
106 | |> Dict.set 4 [4]
107 | |> Dict.set 5 [5,5]
108 | |> Dict.set 6 [6,6]
109 | |> Dict.set 7 [7,7]
110 | |> Dict.set 8 [8,8]
111 | |> Dict.set 9 [9,9]
112 | |> Dict.set 10 [10,10]
113 | |> Dict.set 11 [11]
114 | |> Dict.set 12 [12]
115 | |> Dict.set 13 [13]
116 | |> Dict.set 14 [14]
117 | |> Dict.set 15 [15]
118 | in
119 | describe "merge Tests"
120 | [ test "merge empties" <|
121 | \{} ->
122 | Expect.equal Dict.empty
123 | (Dict.merge Dict.set setBoth Dict.set Dict.empty Dict.empty Dict.empty)
124 | , test "merge singletons in order" <|
125 | \{} ->
126 | Expect.equal ( Dict.empty |> Dict.set "u1" [ 1 ] |> Dict.set "u2" [ 2 ])
127 | (Dict.merge Dict.set setBoth Dict.set s1 s2 Dict.empty)
128 | , test "merge singletons out of order" <|
129 | \{} ->
130 | Expect.equal ( Dict.empty |> Dict.set "u1" [ 1 ] |> Dict.set "u2" [ 2 ])
131 | (Dict.merge Dict.set setBoth Dict.set s2 s1 Dict.empty)
132 | , test "merge with duplicate key" <|
133 | \{} ->
134 | Expect.equal ( Dict.singleton "u2" [ 2, 3 ] )
135 | (Dict.merge Dict.set setBoth Dict.set s2 s23 Dict.empty)
136 | , test "partially overlapping" <|
137 | \{} ->
138 | Expect.equal bExpected
139 | (Dict.merge Dict.set setBoth Dict.set b1 b2 Dict.empty)
140 | ]
141 | in
142 | describe "Dict Tests"
143 | [ buildTests
144 | , queryTests
145 | , combineTests
146 | , transformTests
147 | , mergeTests
148 | ]
149 |
--------------------------------------------------------------------------------
/tests/src/Test/Equality.gren:
--------------------------------------------------------------------------------
1 | module Test.Equality exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Expect
5 | import Fuzz
6 | import Maybe exposing (..)
7 | import Test exposing (..)
8 |
9 |
10 | type Different
11 | = A String
12 | | B (Array Int)
13 |
14 |
15 | tests : Test
16 | tests =
17 | let
18 | diffTests =
19 | describe "ADT equality"
20 | [ test "As eq" <| \{} -> Expect.equal True (A "a" == A "a")
21 | , test "Bs eq" <| \{} -> Expect.equal True (B [ 1 ] == B [ 1 ])
22 | , test "A left neq" <| \{} -> Expect.equal True (A "a" /= B [ 1 ])
23 | , test "A right neq" <| \{} -> Expect.equal True (B [ 1 ] /= A "a")
24 | ]
25 |
26 | recordTests =
27 | describe "Record equality"
28 | [ test "empty same" <| \{} -> Expect.equal True ({} == {})
29 | , test "ctor same" <| \{} -> Expect.equal True ({ field = Just 3 } == { field = Just 3 })
30 | , test "ctor same, special case" <| \{} -> Expect.equal True ({ ctor = Just 3 } == { ctor = Just 3 })
31 | , test "ctor diff" <| \{} -> Expect.equal True ({ field = Just 3 } /= { field = Nothing })
32 | , test "ctor diff, special case" <| \{} -> Expect.equal True ({ ctor = Just 3 } /= { ctor = Nothing })
33 | ]
34 |
35 | arrayTests =
36 | describe "Array equality"
37 | [ fuzz2 (Fuzz.intRange 100 10000) (Fuzz.intRange 100 10000) "Simple comparison" <|
38 | \size1 size2 ->
39 | Expect.equal
40 | (size1 == size2)
41 | (Array.range 0 size1 == Array.range 0 size2)
42 | ]
43 | in
44 | describe "Equality Tests" [ diffTests, recordTests, arrayTests ]
45 |
--------------------------------------------------------------------------------
/tests/src/Test/Json.gren:
--------------------------------------------------------------------------------
1 | module Test.Json exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Expect
5 | import Json.Decode as Json
6 | import Result exposing (..)
7 | import String
8 | import Test exposing (..)
9 |
10 |
11 | tests : Test
12 | tests =
13 | describe "Json decode"
14 | [ intTests
15 | , customTests
16 | ]
17 |
18 |
19 | intTests : Test
20 | intTests =
21 | let
22 | testInt val str =
23 | when Json.decodeString Json.int str is
24 | Ok _ ->
25 | Expect.equal val True
26 |
27 | Err _ ->
28 | Expect.equal val False
29 | in
30 | describe "Json decode int"
31 | [ test "whole int" <| \{} -> testInt True "4"
32 | , test "-whole int" <| \{} -> testInt True "-4"
33 | , test "whole float" <| \{} -> testInt True "4.0"
34 | , test "-whole float" <| \{} -> testInt True "-4.0"
35 | , test "large int" <| \{} -> testInt True "1801439850948"
36 | , test "-large int" <| \{} -> testInt True "-1801439850948"
37 | , test "float" <| \{} -> testInt False "4.2"
38 | , test "-float" <| \{} -> testInt False "-4.2"
39 | , test "Infinity" <| \{} -> testInt False "Infinity"
40 | , test "-Infinity" <| \{} -> testInt False "-Infinity"
41 | , test "NaN" <| \{} -> testInt False "NaN"
42 | , test "-NaN" <| \{} -> testInt False "-NaN"
43 | , test "true" <| \{} -> testInt False "true"
44 | , test "false" <| \{} -> testInt False "false"
45 | , test "string" <| \{} -> testInt False "\"string\""
46 | , test "object" <| \{} -> testInt False "{}"
47 | , test "null" <| \{} -> testInt False "null"
48 | , test "undefined" <| \{} -> testInt False "undefined"
49 | , test "Decoder expects object finds array, was crashing runtime." <|
50 | \{} ->
51 | Expect.equal
52 | (Err "Problem with the given value:\n\n[]\n\nExpecting an OBJECT")
53 | (Result.mapError Json.errorToString (Json.decodeString (Json.dict Json.float) "[]"))
54 | ]
55 |
56 |
57 | customTests : Test
58 | customTests =
59 | let
60 | jsonString =
61 | """
62 | { "foo": "bar" }
63 | """
64 |
65 | customErrorMessage =
66 | "I want to see this message!"
67 |
68 | myDecoder =
69 | Json.field "foo" Json.string |> Json.andThen (\_ -> Json.fail customErrorMessage)
70 |
71 | assertion =
72 | when Json.decodeString myDecoder jsonString is
73 | Ok _ ->
74 | Expect.fail "expected `customDecoder` to produce a value of type Err, but got Ok"
75 |
76 | Err message ->
77 | if String.contains customErrorMessage (Json.errorToString message) then
78 | Expect.pass
79 |
80 | else
81 | Expect.fail <|
82 | "expected `customDecoder` to preserve user's error message '"
83 | ++ customErrorMessage
84 | ++ "', but instead got: "
85 | ++ Json.errorToString message
86 | in
87 | test "customDecoder preserves user error messages" <| \{} -> assertion
88 |
--------------------------------------------------------------------------------
/tests/src/Test/Math.gren:
--------------------------------------------------------------------------------
1 | module Test.Math exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Math exposing (..)
5 | import Expect exposing (FloatingPointTolerance(..))
6 | import Test exposing (..)
7 |
8 |
9 | tests : Test
10 | tests =
11 | let
12 | trigTests =
13 | describe "Trigonometry Tests"
14 | [ test "radians 0" <| \{} -> Expect.equal 0 (radians 0)
15 | , test "radians positive" <| \{} -> Expect.equal 5 (radians 5)
16 | , test "radians negative" <| \{} -> Expect.equal -5 (radians -5)
17 | , test "degrees 0" <| \{} -> Expect.equal 0 (degrees 0)
18 | , test "degrees 90" <| \{} -> Expect.lessThan 0.01 (abs (1.57 - degrees 90))
19 |
20 | -- This should test to enough precision to know if anything's breaking
21 | , test "degrees -145" <| \{} -> Expect.lessThan 0.01 (abs (-2.53 - degrees -145))
22 |
23 | -- This should test to enough precision to know if anything's breaking
24 | , test "turns 0" <| \{} -> Expect.equal 0 (turns 0)
25 | , test "turns 8" <| \{} -> Expect.lessThan 0.01 (abs (50.26 - turns 8))
26 |
27 | -- This should test to enough precision to know if anything's breaking
28 | , test "turns -133" <| \{} -> Expect.lessThan 0.01 (abs (-835.66 - turns -133))
29 |
30 | , test "cos" <| \{} -> Expect.equal 1 (cos 0)
31 | , test "sin" <| \{} -> Expect.equal 0 (sin 0)
32 | , test "tan" <| \{} -> Expect.lessThan 0.01 (abs (12.67 - tan 17.2))
33 | , test "acos" <| \{} -> Expect.lessThan 0.01 (abs (3.14 - acos -1))
34 | , test "asin" <| \{} -> Expect.lessThan 0.01 (abs (0.3 - asin 0.3))
35 | , test "atan" <| \{} -> Expect.lessThan 0.01 (abs (1.57 - atan 4567.8))
36 | , test "atan2" <| \{} -> Expect.lessThan 0.01 (abs (1.55 - atan2 36 0.65))
37 | , test "pi" <| \{} -> Expect.lessThan 0.01 (abs (3.14 - pi))
38 | ]
39 |
40 | basicMathTests =
41 | describe "Basic Math Tests"
42 | [ test "add float" <| \{} -> Expect.equal 159 (155.6 + 3.4)
43 | , test "add int" <| \{} -> Expect.equal 17 (round 10 + round 7)
44 | , test "subtract float" <| \{} -> Expect.within (Absolute 0.00000001) -6.3 (1 - 7.3)
45 | , test "subtract int" <| \{} -> Expect.equal 1130 (round 9432 - round 8302)
46 | , test "multiply float" <| \{} -> Expect.within (Relative 0.00000001) 432 (96 * 4.5)
47 | , test "multiply int" <| \{} -> Expect.equal 90 (round 10 * round 9)
48 | , test "divide float" <| \{} -> Expect.within (Relative 0.00000001) 13.175 (527 / 40)
49 | , test "divide int" <| \{} -> Expect.equal 23 (70 // 3)
50 | , test "7 |> remainderBy 2" <| \{} -> Expect.equal 1 (7 |> remainderBy 2)
51 | , test "-1 |> remainderBy 4" <| \{} -> Expect.equal -1 (-1 |> remainderBy 4)
52 | , test "modBy 2 7" <| \{} -> Expect.equal 1 (modBy 2 7)
53 | , test "modBy 4 -1" <| \{} -> Expect.equal 3 (modBy 4 -1)
54 | , test "3^2" <| \{} -> Expect.equal 9 (3 ^ 2)
55 | , test "sqrt" <| \{} -> Expect.equal 9 (sqrt 81)
56 | , test "negate 42" <| \{} -> Expect.equal -42 (negate 42)
57 | , test "negate -42" <| \{} -> Expect.equal 42 (negate -42)
58 | , test "negate 0" <| \{} -> Expect.equal 0 (negate 0)
59 | , test "abs -25" <| \{} -> Expect.equal 25 (abs -25)
60 | , test "abs 76" <| \{} -> Expect.equal 76 (abs 76)
61 | , test "logBase 10 100" <| \{} -> Expect.equal 2 (logBase 10 100)
62 | , test "logBase 10 1000" <| \{} -> Expect.equal 3 (logBase 10 1000)
63 | , test "logBase 2 256" <| \{} -> Expect.equal 8 (logBase 2 256)
64 | , test "e" <| \{} -> Expect.lessThan 0.01 (abs (2.72 - e))
65 | ]
66 |
67 | conversionTests =
68 | describe "Conversion Tests"
69 | [ test "round 0.6" <| \{} -> Expect.equal 1 (round 0.6)
70 | , test "round 0.4" <| \{} -> Expect.equal 0 (round 0.4)
71 | , test "round 0.5" <| \{} -> Expect.equal 1 (round 0.5)
72 | , test "truncate -2367.9267" <| \{} -> Expect.equal -2367 (truncate -2367.9267)
73 | , test "floor -2367.9267" <| \{} -> Expect.equal -2368 (floor -2367.9267)
74 | , test "ceiling 37.2" <| \{} -> Expect.equal 38 (ceiling 37.2)
75 | , test "toFloat 25" <| \{} -> Expect.equal 25 (toFloat 25)
76 | ]
77 | in
78 | describe "Math"
79 | [ trigTests
80 | , basicMathTests
81 | , conversionTests
82 | ]
83 |
--------------------------------------------------------------------------------
/tests/src/Test/Maybe.gren:
--------------------------------------------------------------------------------
1 | module Test.Maybe exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Expect
5 | import Maybe exposing (..)
6 | import Test exposing (..)
7 | import Math
8 |
9 |
10 | isOdd : Int -> Bool
11 | isOdd n =
12 | Math.remainderBy 2 n /= 0
13 |
14 |
15 | tests : Test
16 | tests =
17 | describe "Maybe Tests"
18 | [ describe "Query"
19 | [ describe "hasValue"
20 | [ test "True case" <|
21 | \{} -> Expect.equal True (Maybe.hasValue 5 (Just 5))
22 | , test "False case" <|
23 | \{} -> Expect.equal False (Maybe.hasValue 5 (Just 3))
24 | , test "Nothing" <|
25 | \{} -> Expect.equal False (Maybe.hasValue 5 Nothing)
26 | ]
27 | , describe "checkValue"
28 | [ test "True case" <|
29 | \{} -> Expect.equal True (Maybe.checkValue isOdd (Just 5))
30 | , test "False case" <|
31 | \{} -> Expect.equal False (Maybe.checkValue isOdd (Just 2))
32 | , test "Nothing" <|
33 | \{} -> Expect.equal False (Maybe.checkValue isOdd Nothing)
34 | ]
35 | ]
36 | , describe "Transform"
37 | [ describe "withDefault"
38 | [ test "no default used" <|
39 | \{} -> Expect.equal 0 (Maybe.withDefault 5 (Just 0))
40 | , test "default used" <|
41 | \{} -> Expect.equal 5 (Maybe.withDefault 5 Nothing)
42 | ]
43 | , describe "withDefaultLazy"
44 | [ test "no default used" <|
45 | \{} -> Expect.equal 0 (Maybe.withDefaultLazy (\{} -> 5) (Just 0))
46 | , test "default used" <|
47 | \{} -> Expect.equal 5 (Maybe.withDefaultLazy (\{} -> 5) Nothing)
48 | ]
49 | , describe "map"
50 | (let
51 | f =
52 | \n -> n + 1
53 | in
54 | [ test "on Just" <|
55 | \{} ->
56 | Expect.equal
57 | (Just 1)
58 | (Maybe.map f (Just 0))
59 | , test "on Nothing" <|
60 | \{} ->
61 | Expect.equal
62 | Nothing
63 | (Maybe.map f Nothing)
64 | ]
65 | )
66 | , describe "map2"
67 | (let
68 | f =
69 | (+)
70 | in
71 | [ test "on (Just, Just)" <|
72 | \{} ->
73 | Expect.equal
74 | (Just 1)
75 | (Maybe.map2 f (Just 0) (Just 1))
76 | , test "on (Just, Nothing)" <|
77 | \{} ->
78 | Expect.equal
79 | Nothing
80 | (Maybe.map2 f (Just 0) Nothing)
81 | , test "on (Nothing, Just)" <|
82 | \{} ->
83 | Expect.equal
84 | Nothing
85 | (Maybe.map2 f Nothing (Just 0))
86 | ]
87 | )
88 | , describe "map3"
89 | (let
90 | f =
91 | \a b c -> a + b + c
92 | in
93 | [ test "on (Just, Just, Just)" <|
94 | \{} ->
95 | Expect.equal
96 | (Just 3)
97 | (Maybe.map3 f (Just 1) (Just 1) (Just 1))
98 | , test "on (Just, Just, Nothing)" <|
99 | \{} ->
100 | Expect.equal
101 | Nothing
102 | (Maybe.map3 f (Just 1) (Just 1) Nothing)
103 | , test "on (Just, Nothing, Just)" <|
104 | \{} ->
105 | Expect.equal
106 | Nothing
107 | (Maybe.map3 f (Just 1) Nothing (Just 1))
108 | , test "on (Nothing, Just, Just)" <|
109 | \{} ->
110 | Expect.equal
111 | Nothing
112 | (Maybe.map3 f Nothing (Just 1) (Just 1))
113 | ]
114 | )
115 | , describe "map4"
116 | (let
117 | f a b c d =
118 | a + b + c + d
119 |
120 | map4 fn a b c d =
121 | Just fn
122 | |> Maybe.andMap a
123 | |> Maybe.andMap b
124 | |> Maybe.andMap c
125 | |> Maybe.andMap d
126 | in
127 | [ test "on (Just, Just, Just, Just)" <|
128 | \{} ->
129 | Expect.equal
130 | (Just 4)
131 | (map4 f (Just 1) (Just 1) (Just 1) (Just 1))
132 | , test "on (Just, Just, Just, Nothing)" <|
133 | \{} ->
134 | Expect.equal
135 | Nothing
136 | (map4 f (Just 1) (Just 1) (Just 1) Nothing)
137 | , test "on (Just, Just, Nothing, Just)" <|
138 | \{} ->
139 | Expect.equal
140 | Nothing
141 | (map4 f (Just 1) (Just 1) Nothing (Just 1))
142 | , test "on (Just, Nothing, Just, Just)" <|
143 | \{} ->
144 | Expect.equal
145 | Nothing
146 | (map4 f (Just 1) Nothing (Just 1) (Just 1))
147 | , test "on (Nothing, Just, Just, Just)" <|
148 | \{} ->
149 | Expect.equal
150 | Nothing
151 | (map4 f Nothing (Just 1) (Just 1) (Just 1))
152 | ]
153 | )
154 | , describe "keepIf"
155 | [ test "Success" <| \{} ->
156 | Expect.equal (Just 5) (Maybe.keepIf isOdd (Just 5))
157 | , test "Failure" <| \{} ->
158 | Expect.equal Nothing (Maybe.keepIf isOdd (Just 4))
159 | ]
160 | , describe "andThen"
161 | [ test "succeeding chain" <|
162 | \{} ->
163 | Expect.equal
164 | (Just 1)
165 | (Maybe.andThen (\a -> Just a) (Just 1))
166 | , test "failing chain (original Maybe failed)" <|
167 | \{} ->
168 | Expect.equal
169 | Nothing
170 | (Maybe.andThen (\a -> Just a) Nothing)
171 | , test "failing chain (chained function failed)" <|
172 | \{} ->
173 | Expect.equal
174 | Nothing
175 | (Maybe.andThen (\a -> Nothing) (Just 1))
176 | ]
177 | ]
178 | ]
179 |
--------------------------------------------------------------------------------
/tests/src/Test/Regex.gren:
--------------------------------------------------------------------------------
1 | module Test.Regex exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import String.Regex as Regex exposing (Regex)
5 | import Expect
6 | import String
7 | import Test exposing (..)
8 |
9 |
10 |
11 | tests : Test
12 | tests =
13 | describe "Regex Tests"
14 | [ test "Regex.replace" <| \{} ->
15 | Regex.replace vowels (\_ -> "_") "abc"
16 | |> Expect.equal "_bc"
17 | , test "Regex.split with never and empty string returns empty string" <| \{} ->
18 | Regex.split Regex.never ""
19 | |> Expect.equal [""]
20 | , test "Regex.split with regex and empty string returns empty string" <| \{} ->
21 | Regex.split vowels ""
22 | |> Expect.equal [""]
23 | , test "Regex.split works with word boundaries" <| \{} ->
24 | Regex.split (Maybe.withDefault Regex.never (Regex.fromString "\\b")) "word-kebab"
25 | |> Expect.equal ["word", "-", "kebab"]
26 | , test "Regex.splitAtMost respects the limit" <| \{} ->
27 | Regex.splitAtMost 2 (Maybe.withDefault Regex.never (Regex.fromString "\\b")) "word-kebab"
28 | |> Expect.equal ["word", "-"]
29 | ]
30 |
31 |
32 | vowels : Regex
33 | vowels =
34 | Regex.fromString "[aeiou]"
35 | |> Maybe.withDefault Regex.never
36 |
--------------------------------------------------------------------------------
/tests/src/Test/Result.gren:
--------------------------------------------------------------------------------
1 | module Test.Result exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Math
5 | import Expect
6 | import Result exposing (Result(..))
7 | import String
8 | import Test exposing (..)
9 |
10 |
11 | isEven n =
12 | if Math.modBy 2 n == 0 then
13 | Ok n
14 |
15 | else
16 | Err "number is odd"
17 |
18 |
19 | toIntResult : String -> Result String Int
20 | toIntResult s =
21 | when String.toInt s is
22 | Just i ->
23 | Ok i
24 |
25 | Nothing ->
26 | Err <| "could not convert string '" ++ s ++ "' to an Int"
27 |
28 |
29 | add3 a b c =
30 | a + b + c
31 |
32 |
33 | add4 a b c d =
34 | a + b + c + d
35 |
36 |
37 | tests : Test
38 | tests =
39 | describe "Result"
40 | [ describe "firstOk"
41 | [ test "Ok" <| \{} -> Expect.equal (Just 5) (Result.firstOk [ Ok 5, Err 0, Ok 10 ])
42 | , test "Err" <| \{} -> Expect.equal Nothing (Result.firstOk [ Err 1, Err 2, Err 3 ])
43 | ]
44 | , describe "allOk"
45 | [ test "Ok" <| \{} -> Expect.equal (Ok [ 0, 1 ]) (Result.allOk [ Ok 0, Ok 1 ])
46 | , test "Err" <| \{} -> Expect.equal (Err [ 0 ]) (Result.allOk [ Ok 5, Err 0, Ok 10 ])
47 | ]
48 | , describe "map"
49 | [ test "Ok" <| \{} -> Expect.equal (Ok 3) (Result.map ((+) 1) (Ok 2))
50 | , test "Err" <| \{} -> Expect.equal (Err "error") (Result.map ((+) 1) (Err "error"))
51 | ]
52 | , describe "mapN"
53 | [ test "map2 Ok" <| \{} -> Expect.equal (Ok 3) (Result.map2 (+) (Ok 1) (Ok 2))
54 | , test "map2 Err" <| \{} -> Expect.equal (Err "x") (Result.map2 (+) (Ok 1) (Err "x"))
55 | , test "map3 Ok" <| \{} -> Expect.equal (Ok 6) (Result.map3 add3 (Ok 1) (Ok 2) (Ok 3))
56 | , test "map3 Err" <| \{} -> Expect.equal (Err "x") (Result.map3 add3 (Ok 1) (Ok 2) (Err "x"))
57 | , test "andMap Ok" <| \{} -> Expect.equal (Ok 10) (Ok add4 |> Result.andMap (Ok 1) |> Result.andMap (Ok 2) |> Result.andMap (Ok 3) |> Result.andMap (Ok 4))
58 | , test "andMap Err" <| \{} -> Expect.equal (Err "x") (Ok add4 |> Result.andMap (Ok 1) |> Result.andMap (Ok 2) |> Result.andMap (Ok 3) |> Result.andMap (Err "x"))
59 | ]
60 | , describe "andThen"
61 | [ test "andThen Ok" <| \{} -> Expect.equal (Ok 42) (toIntResult "42" |> Result.andThen isEven)
62 | , test "andThen first Err" <|
63 | \{} ->
64 | Expect.equal
65 | (Err "could not convert string '4.2' to an Int")
66 | (toIntResult "4.2" |> Result.andThen isEven)
67 | , test "andThen second Err" <|
68 | \{} ->
69 | Expect.equal
70 | (Err "number is odd")
71 | (toIntResult "41" |> Result.andThen isEven)
72 | ]
73 | , describe "onError"
74 | [ test "Ok" <|
75 | \{} ->
76 | Expect.equal
77 | (Ok 42)
78 | (toIntResult "42" |> Result.onError (\_ -> Ok 0))
79 | , test "Err" <|
80 | \{} ->
81 | Expect.equal
82 | (Ok 0)
83 | (toIntResult "4.2" |> Result.onError (\_ -> Ok 0))
84 | ]
85 | ]
86 |
--------------------------------------------------------------------------------
/tests/src/Test/String.gren:
--------------------------------------------------------------------------------
1 | module Test.String exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Expect
5 | import Array
6 | import Maybe exposing (..)
7 | import Result exposing (Result(..))
8 | import String
9 | import Test exposing (..)
10 |
11 |
12 | tests : Test
13 | tests =
14 | let
15 | simpleTests =
16 | describe "Simple Stuff"
17 | [ test "is empty" <| \{} -> Expect.equal True (String.isEmpty "")
18 | , test "is not empty" <| \{} -> Expect.equal True (not (String.isEmpty "the world"))
19 | , test "count" <| \{} -> Expect.equal 11 (String.count "innumerable")
20 | , test "endsWith" <| \{} -> Expect.equal True <| String.endsWith "ship" "spaceship"
21 | , test "reverse" <| \{} -> Expect.equal "desserts" (String.reverse "stressed")
22 | , test "repeat" <| \{} -> Expect.equal "hahaha" (String.repeat 3 "ha")
23 | , test "indexes" <| \{} -> Expect.equal [ 0, 2 ] (String.indices "a" "aha")
24 | , test "empty indexes" <| \{} -> Expect.equal [] (String.indices "" "aha")
25 | ]
26 |
27 | combiningTests =
28 | describe "Combining Strings"
29 | [ test "popFirst non-empty" <| \{} -> Expect.equal (Just { first = 'a', rest = "bc" }) (String.popFirst "abc")
30 | , test "popFirst" <| \{} -> Expect.equal Nothing (String.popFirst "")
31 | , test "prepend" <| \{} -> Expect.equal "butterfly" (String.prepend "butter" "fly")
32 | , test "append 1" <| \{} -> Expect.equal "flybutter" (String.append "butter" "fly")
33 | , test "append 2" <| \{} -> Expect.equal "butter" (String.append "butter" "")
34 | , test "append 3" <| \{} -> Expect.equal "butter" (String.append "" "butter")
35 | , test "split commas" <| \{} -> Expect.equal [ "cat", "dog", "cow" ] (String.split "," "cat,dog,cow")
36 | , test "split slashes" <| \{} -> Expect.equal [ "home", "steve", "Desktop", "" ] (String.split "/" "home/steve/Desktop/")
37 | , test "join spaces" <| \{} -> Expect.equal "cat dog cow" (String.join " " [ "cat", "dog", "cow" ])
38 | , test "join slashes" <| \{} -> Expect.equal "home/steve/Desktop" (String.join "/" [ "home", "steve", "Desktop" ])
39 | , test "slice 1" <| \{} -> Expect.equal "c" (String.slice 2 3 "abcd")
40 | , test "slice 2" <| \{} -> Expect.equal "abc" (String.slice 0 3 "abcd")
41 | , test "slice 3" <| \{} -> Expect.equal "abc" (String.slice 0 -1 "abcd")
42 | , test "slice 4" <| \{} -> Expect.equal "cd" (String.slice -2 4 "abcd")
43 | ]
44 |
45 | intTests =
46 | describe "String.toInt"
47 | [ goodInt "1234" 1234
48 | , goodInt "+1234" 1234
49 | , goodInt "-1234" -1234
50 | , badInt "1.34"
51 | , badInt "1e31"
52 | , badInt "123a"
53 | , goodInt "0123" 123
54 | , badInt "0x001A"
55 | , badInt "0x001a"
56 | , badInt "0xBEEF"
57 | , badInt "0x12.0"
58 | , badInt "0x12an"
59 | ]
60 |
61 | floatTests =
62 | describe "String.toFloat"
63 | [ goodFloat "123" 123
64 | , goodFloat "3.14" 3.14
65 | , goodFloat "+3.14" 3.14
66 | , goodFloat "-3.14" -3.14
67 | , goodFloat "0.12" 0.12
68 | , goodFloat ".12" 0.12
69 | , goodFloat "1e-42" 1.0e-42
70 | , goodFloat "6.022e23" 6.022e23
71 | , goodFloat "6.022E23" 6.022e23
72 | , goodFloat "6.022e+23" 6.022e23
73 | , badFloat "6.022e"
74 | , badFloat "6.022n"
75 | , badFloat "6.022.31"
76 | ]
77 |
78 | queryTests =
79 | describe "String queries"
80 | [ test "any true" <| \{} -> Expect.equal True (String.any Char.isDigit "one is 1")
81 | , test "any false" <| \{} -> Expect.equal False (String.any Char.isDigit "one is one")
82 | , test "all true" <| \{} -> Expect.equal True (String.all Char.isDigit "123")
83 | , test "all false" <| \{} -> Expect.equal False (String.all Char.isDigit "1 2 3")
84 | ]
85 | in
86 | describe "String" [ simpleTests, combiningTests, intTests, floatTests, queryTests ]
87 |
88 |
89 |
90 | -- NUMBER HELPERS
91 |
92 |
93 | goodInt : String -> Int -> Test
94 | goodInt str int =
95 | test str <|
96 | \_ ->
97 | Expect.equal (Just int) (String.toInt str)
98 |
99 |
100 | badInt : String -> Test
101 | badInt str =
102 | test str <|
103 | \_ ->
104 | Expect.equal
105 | Nothing
106 | (String.toInt str)
107 |
108 |
109 | goodFloat : String -> Float -> Test
110 | goodFloat str float =
111 | test str <|
112 | \_ ->
113 | Expect.equal (Just float) (String.toFloat str)
114 |
115 |
116 | badFloat : String -> Test
117 | badFloat str =
118 | test str <|
119 | \_ ->
120 | Expect.equal
121 | Nothing
122 | (String.toFloat str)
123 |
--------------------------------------------------------------------------------