├── .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 | --------------------------------------------------------------------------------