├── .dockerignore ├── .github └── workflows │ ├── ci.yml │ ├── publish-image.yml │ └── publish-package.yml ├── .gitignore ├── .node-version ├── .npmrc ├── Dockerfile ├── Justification.md ├── LICENSE ├── README.md ├── example ├── README.md ├── solution.lc ├── test.js └── test.sh ├── logo ├── logo-black.svg ├── logo-white.svg ├── logo.png └── logo.svg ├── package-lock.json ├── package.json ├── src ├── LC-REPL.js └── lambda-calculus.js ├── tests ├── basics-binary-scott │ ├── solution.txt │ └── test.js ├── basics-church │ ├── solution.txt │ └── test.js ├── counter │ ├── solution.txt │ └── test.js ├── delta-generators │ ├── solution.txt │ └── test.js ├── hello-world │ ├── solution.txt │ └── test.js ├── is-prime-scott │ ├── solution.txt │ └── test.js ├── is-prime │ ├── solution.txt │ └── test.js ├── known-bugs │ ├── solution.txt │ └── test.js ├── multiply │ ├── initialSolution.txt │ ├── solution.txt │ └── test.js ├── negabinary-scott │ ├── solution.lc │ └── test.js ├── prime-sieve │ ├── solution-scott.txt │ ├── solution.txt │ └── test.js └── scott-lists │ ├── solution.txt │ └── test.js └── workspace ├── README.md ├── lc-test.js ├── package-lock.json ├── package.json ├── solution.lc └── test.js /.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !workspace/ 3 | workspace/node_modules/ 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: '16' 16 | cache: 'npm' 17 | - run: npm install 18 | - run: npm test 19 | -------------------------------------------------------------------------------- /.github/workflows/publish-image.yml: -------------------------------------------------------------------------------- 1 | # Manually trigger image build and push. 2 | # This must be done after publishing a new version of the package 3 | # and updating `workspace/package.json` to use it. 4 | name: Push Image 5 | on: 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: Tag for the new image 10 | required: true 11 | jobs: 12 | build-and-push-image: 13 | if: ${{ github.repository == 'codewars/lambda-calculus' }} 14 | name: Build Images 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | packages: write 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - name: Get image tag 23 | run: | 24 | # Remove `v` prefix if given 25 | tag=${{ github.event.inputs.tag }} 26 | echo "IMAGE_TAG=${tag#v}" >> $GITHUB_ENV 27 | shell: bash 28 | 29 | - name: Set up Docker Buildx 30 | uses: docker/setup-buildx-action@v1 31 | 32 | - name: Login to GitHub Container Registry 33 | uses: docker/login-action@v1 34 | with: 35 | registry: ghcr.io 36 | username: ${{ github.repository_owner }} 37 | password: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | - name: Build and push image 40 | uses: docker/build-push-action@v2 41 | with: 42 | context: . 43 | push: true 44 | tags: | 45 | ghcr.io/${{ github.repository_owner }}/lambda-calculus:latest 46 | ghcr.io/${{ github.repository_owner }}/lambda-calculus:${{ env.IMAGE_TAG }} 47 | cache-from: type=gha 48 | cache-to: type=gha,mode=max 49 | -------------------------------------------------------------------------------- /.github/workflows/publish-package.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: '16' 15 | cache: 'npm' 16 | registry-url: https://registry.npmjs.org/ 17 | - run: npm ci 18 | - run: npm publish --access public 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}} 21 | 22 | - uses: actions/setup-node@v2 23 | with: 24 | node-version: '16' 25 | cache: 'npm' 26 | registry-url: https://npm.pkg.github.com/ 27 | - run: npm publish --access public 28 | env: 29 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | .npm 7 | node_modules/ 8 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | v16.13.2 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | message="v%s" 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.13-alpine3.15 2 | 3 | RUN set -ex; \ 4 | adduser -D codewarrior; \ 5 | mkdir -p /workspace; \ 6 | chown codewarrior:codewarrior /workspace; 7 | 8 | COPY --chown=codewarrior:codewarrior workspace/package.json /workspace/package.json 9 | COPY --chown=codewarrior:codewarrior workspace/package-lock.json /workspace/package-lock.json 10 | COPY --chown=codewarrior:codewarrior workspace/lc-test.js /workspace/lc-test.js 11 | 12 | USER codewarrior 13 | WORKDIR /workspace 14 | RUN npm install 15 | -------------------------------------------------------------------------------- /Justification.md: -------------------------------------------------------------------------------- 1 | # Why? 2 | 3 | ### @kazk wrote in July 2021 4 | 5 | Who's interested? What are you planning to do? Why do you think it's good for Codewars? 6 | 7 | Tell me why you guys are interested in it. Tell me why others might be interested in it. If it teaches them something new, that's a plus too. 8 | 9 | # Short answer 10 | 11 | It's good for programmers to know it, and people seem to realise that. It's also perceived as _fun_ to work with. People indeed _are_ gaining an increased understanding of computation, and _are_ applying that in other languages. 12 | 13 | Making it accessible and easy to work with ( ie, solve and author kata in ) can only grow the user base, and we aim to provide that accessibility and easiness - and we have fun doing it. We are also learning a ton doing it. 14 | 15 | What's good for subscribers is ultimately good for Codewars. 16 | 17 | # Supporting anecdotal evidence 18 | 19 | ### @Kacarott wrote in Jul 2021 20 | 21 | \[D\]oes this sound convincing?: 22 | 23 | As of writing this, in katas either directly or thematically related to lambda calculus, there are: 24 | 25 | * 15 katas (10 of those approved) 26 | * Over 2600 solves 27 | * Ratings from 5kyu to 1kyu 28 | * 10 unique authors (not including translations) 29 | 30 | (This is based on a [collection](https://www.codewars.com/collections/60f2bfb58fd43a000ee5cbc5) of all related LC katas I could find. I may have missed some) 31 | 32 | So the base of interested users is already there. By cementing LC as a language, those numbers would only grow. Additional benefits would be: 33 | 34 | * More katas published, as the difficulty in authoring is lowered (auto compile, no syntax checking, etc) 35 | * More consistent difficulty levels, as LC ‘basics’ (eg. currying/church encoding) can become ‘assumed knowledge’ for harder katas. 36 | * Katas can focus on one thing, rather than needing to reintroduce the entire field of LC 37 | 38 | Benefits for new users: 39 | 40 | * Learning LC greatly facilitates understanding of functional languages 41 | * The ‘fun’ of an esoteric style language, but which builds real understanding and knowledge ( to be applied in other languages ) 42 | 43 | Benefits for Codewars: 44 | 45 | * Increases catalogue of available languages 46 | * Adds to the low number of ‘esoteric style’ languages 47 | * Probably is the only programming site around that offers it 48 | 49 | ### @JohanWiltink wrote in Jul 2021 50 | 51 | I don't know if Kazk is going to be impressed with 2600 solves and the other numbers - but I am. I'm also not sure if Kazk sees yet another language as an advantage or rather as a disadvantage, but I think LC, as "functional assembly", would be an asset to CW. Kazk may want more big languages rather than myriad small ones, but this is a very fundamental small language. 52 | 53 | ### @JomoPipi wrote in Aug 2021 54 | 55 | It shows that you can create anything from almost nothing, and it's actually fun to use and not annoying like BF. 56 | 57 | It may seem like a very limited low level language, but once you write a couple of functions it oddly becomes like a high level language that is capable of supporting elegant code. 58 | 59 | Personally, I think it's beautiful and it helped me become a better programmer. 60 | 61 | @JohanWiltink: I can only fully agree with these sentiments; I feel exactly the same way. 62 | 63 | ### [James Iry](https://www.blogger.com/profile/02835376424060382389) [wrote](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) in 2009 64 | 65 | 1936 - Alan Turing invents every programming language that will ever be but is shanghaied by British Intelligence to be 007 before he can patent them. 66 | 67 | 1936 - Alonzo Church also invents every language that will ever be _but does it better._ His lambda calculus is ignored because it is insufficiently C-like. This criticism occurs in spite of the fact that C has not yet been invented. 68 | 69 | @JohanWiltink: my emphasis. Lambda Calculus encodes Life, The Universe and Everything, and does it _well._ 70 | 71 | ### Update Jan 2022 72 | 73 | In Jan 2022 74 | * a new [LC Factorial](https://www.codewars.com/kata/factorial-lambda-calculus) kata achieved 17 unique solvers in a week ( .. including the author ) 75 | * [#lambda-calculus](https://discord.com/channels/846624424199061524/933847946808684596) was added in Discord - and generated this priceless comment: 76 | 77 | [@Hobovsky](https://discord.com/channels/846624424199061524/933847946808684596/934109111740690472) 78 | 79 | Me: Let's see what's going on in #lambda-calculus , i am sure they talk some interesting stuff there and i will learn something cool 80 | Karrot: ... so I call this function with an infinite amount of arguments, reversed, and... 81 | Me: ... imma fukk outta here, kthxbye 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Johan Wiltink and Keldan Chapman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LC Compiler for Codewars 2 | 3 | ![logo-black](./logo/logo-black.svg#gh-light-mode-only) 4 | ![logo-white](./logo/logo-white.svg#gh-dark-mode-only) 5 | 6 | Written by [Kacarott](https://github.com/Kacarott) and [JohanWiltink](https://github.com/JohanWiltink) 7 | 8 | ### Install 9 | 10 | ```bash 11 | $ npm i --save @codewars/lambda-calculus 12 | ``` 13 | 14 | ### Usage 15 | 16 | > NOTE: When writing tests on Codewars, you can use the predefined wrapper module "./files.js" to get 17 | > the solution file instead of using `fs` like below. 18 | 19 | ```javascript 20 | import { readFileSync } from "fs"; 21 | // Import module 22 | import * as LC from "@codewars/lambda-calculus"; 23 | 24 | // Set config options 25 | LC.config.purity = "Let"; 26 | LC.config.numEncoding = "Church"; 27 | 28 | const code = readFileSync("solution.lc", {encoding: "utf8"}); 29 | // Compile 30 | const solution = compile(code).TRUE; 31 | // or 32 | const {TRUE,FALSE} = compile(code); 33 | 34 | // Use 35 | console.log(solution(true)(false)); // true 36 | // or 37 | console.log(TRUE(true)(false)) // true 38 | ``` 39 | 40 | ### Documentation 41 | 42 | 43 | --- 44 | 45 | `compile :: String -> {String: (Term -> Term)}` 46 | 47 | `compile` is the main entry point of the module. Compiles the specified text according the current `config` options, and returns an object binding all defined terms to their corresponding functions. 48 | 49 | --- 50 | 51 | `config :: {String: String}` 52 | 53 | `config` is an object which provides the interface for controlling how compilation behaves. Currently there are three configurable properties: `purity`, `numEncoding` and `verbosity`. 54 | 55 | | Property | Option | Description | 56 | | -------- | ---- | ---- | 57 | | `purity` | `Let` | Allows definition of named terms which can be used in subsequent definitions. Does *not* support recursion. | 58 | | | `LetRec` | The same as `Let`, but additionally supporting direct recursion. | 59 | | | `PureLC` (default) | Pure lambda calculus only. Terms are still named so that they can be accessed by tests, but named terms may not be used elsewhere. | 60 | | `numEncoding` | `None` | No number encoding. Use of number literals will cause an error. | 61 | | | `Church`
`Scott`
`BinaryScott` | Number literals will be automatically converted to corresponding LC terms, according to the encoding chosen. | 62 | | `verbosity` | `Calm`
`Concise`
`Loquacious`
`Verbose` | Controls the amount of information that will be reported during compilation. | 63 | 64 | ### Container Image 65 | 66 | The container image used by the Code Runner is available on [GHCR](https://github.com/codewars/lambda-calculus/pkgs/container/lambda-calculus). 67 | 68 | ```bash 69 | docker pull ghcr.io/codewars/lambda-calculus:latest 70 | ``` 71 | 72 | #### Building 73 | 74 | The image can be built from this repository: 75 | 76 | ```bash 77 | docker build -t ghcr.io/codewars/lambda-calculus:latest . 78 | ``` 79 | 80 | #### Usage 81 | 82 | See [example/](./example/) to learn how to use the image to test locally. 83 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Minimal example with a solution and tests. 2 | 3 | Use `./test.sh` to run this in a container. 4 | -------------------------------------------------------------------------------- /example/solution.lc: -------------------------------------------------------------------------------- 1 | # Kacarott 2 | 3 | true = \ a b . a 4 | false = \ a b . b 5 | 6 | zero = false 7 | succ = \ n f x . f (n f x) 8 | 9 | y = \ f . (\ x . f (x x)) (\ x . f (x x)) 10 | 11 | counter = y (\ count n b . b (count (succ n)) (n) ) zero 12 | -------------------------------------------------------------------------------- /example/test.js: -------------------------------------------------------------------------------- 1 | import { assert, LC, getSolution } from "./lc-test.js"; 2 | 3 | LC.configure({ purity: "Let", numEncoding: "Church" }); 4 | const { counter } = LC.compile(getSolution()); 5 | 6 | const T = t => _ => t; 7 | const F = _ => f => f; 8 | 9 | describe("counter", () => { 10 | it("fixed tests", () => { 11 | assert.equal( counter(T)(T)(T)(F), 3); 12 | assert.equal( counter(T)(F), 1); 13 | assert.equal( counter(T)(T)(T)(T)(T)(T)(T)(F), 7); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /example/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Test example in a container similar to the runner 3 | W=/workspace/ 4 | C=$(docker container create --rm -w $W ghcr.io/codewars/lambda-calculus:latest npx mocha) 5 | docker container cp ./solution.lc $C:$W 6 | docker container cp ./test.js $C:$W 7 | docker container start --attach $C 8 | -------------------------------------------------------------------------------- /logo/logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /logo/logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewars/lambda-calculus/3993e3e9d5285cfa00b22c32df12a1e031cb2226/logo/logo.png -------------------------------------------------------------------------------- /logo/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 42 | 44 | 49 | 53 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@codewars/lambda-calculus", 3 | "version": "1.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@codewars/lambda-calculus", 9 | "version": "1.1.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "chai": "^4.3.4", 13 | "mocha": "^9.1.4" 14 | } 15 | }, 16 | "node_modules/@ungap/promise-all-settled": { 17 | "version": "1.1.2", 18 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 19 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 20 | "dev": true 21 | }, 22 | "node_modules/ansi-colors": { 23 | "version": "4.1.1", 24 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 25 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 26 | "dev": true, 27 | "engines": { 28 | "node": ">=6" 29 | } 30 | }, 31 | "node_modules/ansi-regex": { 32 | "version": "5.0.1", 33 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 34 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 35 | "dev": true, 36 | "engines": { 37 | "node": ">=8" 38 | } 39 | }, 40 | "node_modules/ansi-styles": { 41 | "version": "4.3.0", 42 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 43 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 44 | "dev": true, 45 | "dependencies": { 46 | "color-convert": "^2.0.1" 47 | }, 48 | "engines": { 49 | "node": ">=8" 50 | }, 51 | "funding": { 52 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 53 | } 54 | }, 55 | "node_modules/anymatch": { 56 | "version": "3.1.2", 57 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 58 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 59 | "dev": true, 60 | "dependencies": { 61 | "normalize-path": "^3.0.0", 62 | "picomatch": "^2.0.4" 63 | }, 64 | "engines": { 65 | "node": ">= 8" 66 | } 67 | }, 68 | "node_modules/argparse": { 69 | "version": "2.0.1", 70 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 71 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 72 | "dev": true 73 | }, 74 | "node_modules/assertion-error": { 75 | "version": "1.1.0", 76 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 77 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 78 | "dev": true, 79 | "engines": { 80 | "node": "*" 81 | } 82 | }, 83 | "node_modules/balanced-match": { 84 | "version": "1.0.2", 85 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 86 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 87 | "dev": true 88 | }, 89 | "node_modules/binary-extensions": { 90 | "version": "2.2.0", 91 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 92 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 93 | "dev": true, 94 | "engines": { 95 | "node": ">=8" 96 | } 97 | }, 98 | "node_modules/brace-expansion": { 99 | "version": "1.1.11", 100 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 101 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 102 | "dev": true, 103 | "dependencies": { 104 | "balanced-match": "^1.0.0", 105 | "concat-map": "0.0.1" 106 | } 107 | }, 108 | "node_modules/braces": { 109 | "version": "3.0.2", 110 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 111 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 112 | "dev": true, 113 | "dependencies": { 114 | "fill-range": "^7.0.1" 115 | }, 116 | "engines": { 117 | "node": ">=8" 118 | } 119 | }, 120 | "node_modules/browser-stdout": { 121 | "version": "1.3.1", 122 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 123 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 124 | "dev": true 125 | }, 126 | "node_modules/camelcase": { 127 | "version": "6.3.0", 128 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 129 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 130 | "dev": true, 131 | "engines": { 132 | "node": ">=10" 133 | }, 134 | "funding": { 135 | "url": "https://github.com/sponsors/sindresorhus" 136 | } 137 | }, 138 | "node_modules/chai": { 139 | "version": "4.3.4", 140 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", 141 | "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", 142 | "dev": true, 143 | "dependencies": { 144 | "assertion-error": "^1.1.0", 145 | "check-error": "^1.0.2", 146 | "deep-eql": "^3.0.1", 147 | "get-func-name": "^2.0.0", 148 | "pathval": "^1.1.1", 149 | "type-detect": "^4.0.5" 150 | }, 151 | "engines": { 152 | "node": ">=4" 153 | } 154 | }, 155 | "node_modules/chalk": { 156 | "version": "4.1.2", 157 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 158 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 159 | "dev": true, 160 | "dependencies": { 161 | "ansi-styles": "^4.1.0", 162 | "supports-color": "^7.1.0" 163 | }, 164 | "engines": { 165 | "node": ">=10" 166 | }, 167 | "funding": { 168 | "url": "https://github.com/chalk/chalk?sponsor=1" 169 | } 170 | }, 171 | "node_modules/chalk/node_modules/supports-color": { 172 | "version": "7.2.0", 173 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 174 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 175 | "dev": true, 176 | "dependencies": { 177 | "has-flag": "^4.0.0" 178 | }, 179 | "engines": { 180 | "node": ">=8" 181 | } 182 | }, 183 | "node_modules/check-error": { 184 | "version": "1.0.2", 185 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 186 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 187 | "dev": true, 188 | "engines": { 189 | "node": "*" 190 | } 191 | }, 192 | "node_modules/chokidar": { 193 | "version": "3.5.2", 194 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", 195 | "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", 196 | "dev": true, 197 | "dependencies": { 198 | "anymatch": "~3.1.2", 199 | "braces": "~3.0.2", 200 | "glob-parent": "~5.1.2", 201 | "is-binary-path": "~2.1.0", 202 | "is-glob": "~4.0.1", 203 | "normalize-path": "~3.0.0", 204 | "readdirp": "~3.6.0" 205 | }, 206 | "engines": { 207 | "node": ">= 8.10.0" 208 | }, 209 | "optionalDependencies": { 210 | "fsevents": "~2.3.2" 211 | } 212 | }, 213 | "node_modules/cliui": { 214 | "version": "7.0.4", 215 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 216 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 217 | "dev": true, 218 | "dependencies": { 219 | "string-width": "^4.2.0", 220 | "strip-ansi": "^6.0.0", 221 | "wrap-ansi": "^7.0.0" 222 | } 223 | }, 224 | "node_modules/color-convert": { 225 | "version": "2.0.1", 226 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 227 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 228 | "dev": true, 229 | "dependencies": { 230 | "color-name": "~1.1.4" 231 | }, 232 | "engines": { 233 | "node": ">=7.0.0" 234 | } 235 | }, 236 | "node_modules/color-name": { 237 | "version": "1.1.4", 238 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 239 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 240 | "dev": true 241 | }, 242 | "node_modules/concat-map": { 243 | "version": "0.0.1", 244 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 245 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 246 | "dev": true 247 | }, 248 | "node_modules/debug": { 249 | "version": "4.3.2", 250 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 251 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 252 | "dev": true, 253 | "dependencies": { 254 | "ms": "2.1.2" 255 | }, 256 | "engines": { 257 | "node": ">=6.0" 258 | }, 259 | "peerDependenciesMeta": { 260 | "supports-color": { 261 | "optional": true 262 | } 263 | } 264 | }, 265 | "node_modules/debug/node_modules/ms": { 266 | "version": "2.1.2", 267 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 268 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 269 | "dev": true 270 | }, 271 | "node_modules/decamelize": { 272 | "version": "4.0.0", 273 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 274 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 275 | "dev": true, 276 | "engines": { 277 | "node": ">=10" 278 | }, 279 | "funding": { 280 | "url": "https://github.com/sponsors/sindresorhus" 281 | } 282 | }, 283 | "node_modules/deep-eql": { 284 | "version": "3.0.1", 285 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 286 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 287 | "dev": true, 288 | "dependencies": { 289 | "type-detect": "^4.0.0" 290 | }, 291 | "engines": { 292 | "node": ">=0.12" 293 | } 294 | }, 295 | "node_modules/diff": { 296 | "version": "5.0.0", 297 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 298 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 299 | "dev": true, 300 | "engines": { 301 | "node": ">=0.3.1" 302 | } 303 | }, 304 | "node_modules/emoji-regex": { 305 | "version": "8.0.0", 306 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 307 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 308 | "dev": true 309 | }, 310 | "node_modules/escalade": { 311 | "version": "3.1.1", 312 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 313 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 314 | "dev": true, 315 | "engines": { 316 | "node": ">=6" 317 | } 318 | }, 319 | "node_modules/escape-string-regexp": { 320 | "version": "4.0.0", 321 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 322 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 323 | "dev": true, 324 | "engines": { 325 | "node": ">=10" 326 | }, 327 | "funding": { 328 | "url": "https://github.com/sponsors/sindresorhus" 329 | } 330 | }, 331 | "node_modules/fill-range": { 332 | "version": "7.0.1", 333 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 334 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 335 | "dev": true, 336 | "dependencies": { 337 | "to-regex-range": "^5.0.1" 338 | }, 339 | "engines": { 340 | "node": ">=8" 341 | } 342 | }, 343 | "node_modules/find-up": { 344 | "version": "5.0.0", 345 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 346 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 347 | "dev": true, 348 | "dependencies": { 349 | "locate-path": "^6.0.0", 350 | "path-exists": "^4.0.0" 351 | }, 352 | "engines": { 353 | "node": ">=10" 354 | }, 355 | "funding": { 356 | "url": "https://github.com/sponsors/sindresorhus" 357 | } 358 | }, 359 | "node_modules/flat": { 360 | "version": "5.0.2", 361 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 362 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 363 | "dev": true, 364 | "bin": { 365 | "flat": "cli.js" 366 | } 367 | }, 368 | "node_modules/fs.realpath": { 369 | "version": "1.0.0", 370 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 371 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 372 | "dev": true 373 | }, 374 | "node_modules/fsevents": { 375 | "version": "2.3.2", 376 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 377 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 378 | "dev": true, 379 | "hasInstallScript": true, 380 | "optional": true, 381 | "os": [ 382 | "darwin" 383 | ], 384 | "engines": { 385 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 386 | } 387 | }, 388 | "node_modules/get-caller-file": { 389 | "version": "2.0.5", 390 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 391 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 392 | "dev": true, 393 | "engines": { 394 | "node": "6.* || 8.* || >= 10.*" 395 | } 396 | }, 397 | "node_modules/get-func-name": { 398 | "version": "2.0.0", 399 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 400 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 401 | "dev": true, 402 | "engines": { 403 | "node": "*" 404 | } 405 | }, 406 | "node_modules/glob": { 407 | "version": "7.1.7", 408 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", 409 | "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", 410 | "dev": true, 411 | "dependencies": { 412 | "fs.realpath": "^1.0.0", 413 | "inflight": "^1.0.4", 414 | "inherits": "2", 415 | "minimatch": "^3.0.4", 416 | "once": "^1.3.0", 417 | "path-is-absolute": "^1.0.0" 418 | }, 419 | "engines": { 420 | "node": "*" 421 | }, 422 | "funding": { 423 | "url": "https://github.com/sponsors/isaacs" 424 | } 425 | }, 426 | "node_modules/glob-parent": { 427 | "version": "5.1.2", 428 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 429 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 430 | "dev": true, 431 | "dependencies": { 432 | "is-glob": "^4.0.1" 433 | }, 434 | "engines": { 435 | "node": ">= 6" 436 | } 437 | }, 438 | "node_modules/growl": { 439 | "version": "1.10.5", 440 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 441 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 442 | "dev": true, 443 | "engines": { 444 | "node": ">=4.x" 445 | } 446 | }, 447 | "node_modules/has-flag": { 448 | "version": "4.0.0", 449 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 450 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 451 | "dev": true, 452 | "engines": { 453 | "node": ">=8" 454 | } 455 | }, 456 | "node_modules/he": { 457 | "version": "1.2.0", 458 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 459 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 460 | "dev": true, 461 | "bin": { 462 | "he": "bin/he" 463 | } 464 | }, 465 | "node_modules/inflight": { 466 | "version": "1.0.6", 467 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 468 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 469 | "dev": true, 470 | "dependencies": { 471 | "once": "^1.3.0", 472 | "wrappy": "1" 473 | } 474 | }, 475 | "node_modules/inherits": { 476 | "version": "2.0.4", 477 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 478 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 479 | "dev": true 480 | }, 481 | "node_modules/is-binary-path": { 482 | "version": "2.1.0", 483 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 484 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 485 | "dev": true, 486 | "dependencies": { 487 | "binary-extensions": "^2.0.0" 488 | }, 489 | "engines": { 490 | "node": ">=8" 491 | } 492 | }, 493 | "node_modules/is-extglob": { 494 | "version": "2.1.1", 495 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 496 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 497 | "dev": true, 498 | "engines": { 499 | "node": ">=0.10.0" 500 | } 501 | }, 502 | "node_modules/is-fullwidth-code-point": { 503 | "version": "3.0.0", 504 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 505 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 506 | "dev": true, 507 | "engines": { 508 | "node": ">=8" 509 | } 510 | }, 511 | "node_modules/is-glob": { 512 | "version": "4.0.3", 513 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 514 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 515 | "dev": true, 516 | "dependencies": { 517 | "is-extglob": "^2.1.1" 518 | }, 519 | "engines": { 520 | "node": ">=0.10.0" 521 | } 522 | }, 523 | "node_modules/is-number": { 524 | "version": "7.0.0", 525 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 526 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 527 | "dev": true, 528 | "engines": { 529 | "node": ">=0.12.0" 530 | } 531 | }, 532 | "node_modules/is-plain-obj": { 533 | "version": "2.1.0", 534 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 535 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 536 | "dev": true, 537 | "engines": { 538 | "node": ">=8" 539 | } 540 | }, 541 | "node_modules/is-unicode-supported": { 542 | "version": "0.1.0", 543 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 544 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 545 | "dev": true, 546 | "engines": { 547 | "node": ">=10" 548 | }, 549 | "funding": { 550 | "url": "https://github.com/sponsors/sindresorhus" 551 | } 552 | }, 553 | "node_modules/isexe": { 554 | "version": "2.0.0", 555 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 556 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 557 | "dev": true 558 | }, 559 | "node_modules/js-yaml": { 560 | "version": "4.1.0", 561 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 562 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 563 | "dev": true, 564 | "dependencies": { 565 | "argparse": "^2.0.1" 566 | }, 567 | "bin": { 568 | "js-yaml": "bin/js-yaml.js" 569 | } 570 | }, 571 | "node_modules/locate-path": { 572 | "version": "6.0.0", 573 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 574 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 575 | "dev": true, 576 | "dependencies": { 577 | "p-locate": "^5.0.0" 578 | }, 579 | "engines": { 580 | "node": ">=10" 581 | }, 582 | "funding": { 583 | "url": "https://github.com/sponsors/sindresorhus" 584 | } 585 | }, 586 | "node_modules/log-symbols": { 587 | "version": "4.1.0", 588 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 589 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 590 | "dev": true, 591 | "dependencies": { 592 | "chalk": "^4.1.0", 593 | "is-unicode-supported": "^0.1.0" 594 | }, 595 | "engines": { 596 | "node": ">=10" 597 | }, 598 | "funding": { 599 | "url": "https://github.com/sponsors/sindresorhus" 600 | } 601 | }, 602 | "node_modules/minimatch": { 603 | "version": "3.0.4", 604 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 605 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 606 | "dev": true, 607 | "dependencies": { 608 | "brace-expansion": "^1.1.7" 609 | }, 610 | "engines": { 611 | "node": "*" 612 | } 613 | }, 614 | "node_modules/mocha": { 615 | "version": "9.1.4", 616 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", 617 | "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", 618 | "dev": true, 619 | "dependencies": { 620 | "@ungap/promise-all-settled": "1.1.2", 621 | "ansi-colors": "4.1.1", 622 | "browser-stdout": "1.3.1", 623 | "chokidar": "3.5.2", 624 | "debug": "4.3.2", 625 | "diff": "5.0.0", 626 | "escape-string-regexp": "4.0.0", 627 | "find-up": "5.0.0", 628 | "glob": "7.1.7", 629 | "growl": "1.10.5", 630 | "he": "1.2.0", 631 | "js-yaml": "4.1.0", 632 | "log-symbols": "4.1.0", 633 | "minimatch": "3.0.4", 634 | "ms": "2.1.3", 635 | "nanoid": "3.1.25", 636 | "serialize-javascript": "6.0.0", 637 | "strip-json-comments": "3.1.1", 638 | "supports-color": "8.1.1", 639 | "which": "2.0.2", 640 | "workerpool": "6.1.5", 641 | "yargs": "16.2.0", 642 | "yargs-parser": "20.2.4", 643 | "yargs-unparser": "2.0.0" 644 | }, 645 | "bin": { 646 | "_mocha": "bin/_mocha", 647 | "mocha": "bin/mocha" 648 | }, 649 | "engines": { 650 | "node": ">= 12.0.0" 651 | }, 652 | "funding": { 653 | "type": "opencollective", 654 | "url": "https://opencollective.com/mochajs" 655 | } 656 | }, 657 | "node_modules/ms": { 658 | "version": "2.1.3", 659 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 660 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 661 | "dev": true 662 | }, 663 | "node_modules/nanoid": { 664 | "version": "3.1.25", 665 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", 666 | "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", 667 | "dev": true, 668 | "bin": { 669 | "nanoid": "bin/nanoid.cjs" 670 | }, 671 | "engines": { 672 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 673 | } 674 | }, 675 | "node_modules/normalize-path": { 676 | "version": "3.0.0", 677 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 678 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 679 | "dev": true, 680 | "engines": { 681 | "node": ">=0.10.0" 682 | } 683 | }, 684 | "node_modules/once": { 685 | "version": "1.4.0", 686 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 687 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 688 | "dev": true, 689 | "dependencies": { 690 | "wrappy": "1" 691 | } 692 | }, 693 | "node_modules/p-limit": { 694 | "version": "3.1.0", 695 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 696 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 697 | "dev": true, 698 | "dependencies": { 699 | "yocto-queue": "^0.1.0" 700 | }, 701 | "engines": { 702 | "node": ">=10" 703 | }, 704 | "funding": { 705 | "url": "https://github.com/sponsors/sindresorhus" 706 | } 707 | }, 708 | "node_modules/p-locate": { 709 | "version": "5.0.0", 710 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 711 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 712 | "dev": true, 713 | "dependencies": { 714 | "p-limit": "^3.0.2" 715 | }, 716 | "engines": { 717 | "node": ">=10" 718 | }, 719 | "funding": { 720 | "url": "https://github.com/sponsors/sindresorhus" 721 | } 722 | }, 723 | "node_modules/path-exists": { 724 | "version": "4.0.0", 725 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 726 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 727 | "dev": true, 728 | "engines": { 729 | "node": ">=8" 730 | } 731 | }, 732 | "node_modules/path-is-absolute": { 733 | "version": "1.0.1", 734 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 735 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 736 | "dev": true, 737 | "engines": { 738 | "node": ">=0.10.0" 739 | } 740 | }, 741 | "node_modules/pathval": { 742 | "version": "1.1.1", 743 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 744 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 745 | "dev": true, 746 | "engines": { 747 | "node": "*" 748 | } 749 | }, 750 | "node_modules/picomatch": { 751 | "version": "2.3.1", 752 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 753 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 754 | "dev": true, 755 | "engines": { 756 | "node": ">=8.6" 757 | }, 758 | "funding": { 759 | "url": "https://github.com/sponsors/jonschlinkert" 760 | } 761 | }, 762 | "node_modules/randombytes": { 763 | "version": "2.1.0", 764 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 765 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 766 | "dev": true, 767 | "dependencies": { 768 | "safe-buffer": "^5.1.0" 769 | } 770 | }, 771 | "node_modules/readdirp": { 772 | "version": "3.6.0", 773 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 774 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 775 | "dev": true, 776 | "dependencies": { 777 | "picomatch": "^2.2.1" 778 | }, 779 | "engines": { 780 | "node": ">=8.10.0" 781 | } 782 | }, 783 | "node_modules/require-directory": { 784 | "version": "2.1.1", 785 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 786 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 787 | "dev": true, 788 | "engines": { 789 | "node": ">=0.10.0" 790 | } 791 | }, 792 | "node_modules/safe-buffer": { 793 | "version": "5.2.1", 794 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 795 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 796 | "dev": true, 797 | "funding": [ 798 | { 799 | "type": "github", 800 | "url": "https://github.com/sponsors/feross" 801 | }, 802 | { 803 | "type": "patreon", 804 | "url": "https://www.patreon.com/feross" 805 | }, 806 | { 807 | "type": "consulting", 808 | "url": "https://feross.org/support" 809 | } 810 | ] 811 | }, 812 | "node_modules/serialize-javascript": { 813 | "version": "6.0.0", 814 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 815 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 816 | "dev": true, 817 | "dependencies": { 818 | "randombytes": "^2.1.0" 819 | } 820 | }, 821 | "node_modules/string-width": { 822 | "version": "4.2.3", 823 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 824 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 825 | "dev": true, 826 | "dependencies": { 827 | "emoji-regex": "^8.0.0", 828 | "is-fullwidth-code-point": "^3.0.0", 829 | "strip-ansi": "^6.0.1" 830 | }, 831 | "engines": { 832 | "node": ">=8" 833 | } 834 | }, 835 | "node_modules/strip-ansi": { 836 | "version": "6.0.1", 837 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 838 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 839 | "dev": true, 840 | "dependencies": { 841 | "ansi-regex": "^5.0.1" 842 | }, 843 | "engines": { 844 | "node": ">=8" 845 | } 846 | }, 847 | "node_modules/strip-json-comments": { 848 | "version": "3.1.1", 849 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 850 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 851 | "dev": true, 852 | "engines": { 853 | "node": ">=8" 854 | }, 855 | "funding": { 856 | "url": "https://github.com/sponsors/sindresorhus" 857 | } 858 | }, 859 | "node_modules/supports-color": { 860 | "version": "8.1.1", 861 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 862 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 863 | "dev": true, 864 | "dependencies": { 865 | "has-flag": "^4.0.0" 866 | }, 867 | "engines": { 868 | "node": ">=10" 869 | }, 870 | "funding": { 871 | "url": "https://github.com/chalk/supports-color?sponsor=1" 872 | } 873 | }, 874 | "node_modules/to-regex-range": { 875 | "version": "5.0.1", 876 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 877 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 878 | "dev": true, 879 | "dependencies": { 880 | "is-number": "^7.0.0" 881 | }, 882 | "engines": { 883 | "node": ">=8.0" 884 | } 885 | }, 886 | "node_modules/type-detect": { 887 | "version": "4.0.8", 888 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 889 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 890 | "dev": true, 891 | "engines": { 892 | "node": ">=4" 893 | } 894 | }, 895 | "node_modules/which": { 896 | "version": "2.0.2", 897 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 898 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 899 | "dev": true, 900 | "dependencies": { 901 | "isexe": "^2.0.0" 902 | }, 903 | "bin": { 904 | "node-which": "bin/node-which" 905 | }, 906 | "engines": { 907 | "node": ">= 8" 908 | } 909 | }, 910 | "node_modules/workerpool": { 911 | "version": "6.1.5", 912 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", 913 | "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", 914 | "dev": true 915 | }, 916 | "node_modules/wrap-ansi": { 917 | "version": "7.0.0", 918 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 919 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 920 | "dev": true, 921 | "dependencies": { 922 | "ansi-styles": "^4.0.0", 923 | "string-width": "^4.1.0", 924 | "strip-ansi": "^6.0.0" 925 | }, 926 | "engines": { 927 | "node": ">=10" 928 | }, 929 | "funding": { 930 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 931 | } 932 | }, 933 | "node_modules/wrappy": { 934 | "version": "1.0.2", 935 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 936 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 937 | "dev": true 938 | }, 939 | "node_modules/y18n": { 940 | "version": "5.0.8", 941 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 942 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 943 | "dev": true, 944 | "engines": { 945 | "node": ">=10" 946 | } 947 | }, 948 | "node_modules/yargs": { 949 | "version": "16.2.0", 950 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 951 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 952 | "dev": true, 953 | "dependencies": { 954 | "cliui": "^7.0.2", 955 | "escalade": "^3.1.1", 956 | "get-caller-file": "^2.0.5", 957 | "require-directory": "^2.1.1", 958 | "string-width": "^4.2.0", 959 | "y18n": "^5.0.5", 960 | "yargs-parser": "^20.2.2" 961 | }, 962 | "engines": { 963 | "node": ">=10" 964 | } 965 | }, 966 | "node_modules/yargs-parser": { 967 | "version": "20.2.4", 968 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 969 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 970 | "dev": true, 971 | "engines": { 972 | "node": ">=10" 973 | } 974 | }, 975 | "node_modules/yargs-unparser": { 976 | "version": "2.0.0", 977 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 978 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 979 | "dev": true, 980 | "dependencies": { 981 | "camelcase": "^6.0.0", 982 | "decamelize": "^4.0.0", 983 | "flat": "^5.0.2", 984 | "is-plain-obj": "^2.1.0" 985 | }, 986 | "engines": { 987 | "node": ">=10" 988 | } 989 | }, 990 | "node_modules/yocto-queue": { 991 | "version": "0.1.0", 992 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 993 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 994 | "dev": true, 995 | "engines": { 996 | "node": ">=10" 997 | }, 998 | "funding": { 999 | "url": "https://github.com/sponsors/sindresorhus" 1000 | } 1001 | } 1002 | }, 1003 | "dependencies": { 1004 | "@ungap/promise-all-settled": { 1005 | "version": "1.1.2", 1006 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 1007 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 1008 | "dev": true 1009 | }, 1010 | "ansi-colors": { 1011 | "version": "4.1.1", 1012 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 1013 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 1014 | "dev": true 1015 | }, 1016 | "ansi-regex": { 1017 | "version": "5.0.1", 1018 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1019 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1020 | "dev": true 1021 | }, 1022 | "ansi-styles": { 1023 | "version": "4.3.0", 1024 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1025 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1026 | "dev": true, 1027 | "requires": { 1028 | "color-convert": "^2.0.1" 1029 | } 1030 | }, 1031 | "anymatch": { 1032 | "version": "3.1.2", 1033 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1034 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1035 | "dev": true, 1036 | "requires": { 1037 | "normalize-path": "^3.0.0", 1038 | "picomatch": "^2.0.4" 1039 | } 1040 | }, 1041 | "argparse": { 1042 | "version": "2.0.1", 1043 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1044 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1045 | "dev": true 1046 | }, 1047 | "assertion-error": { 1048 | "version": "1.1.0", 1049 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 1050 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 1051 | "dev": true 1052 | }, 1053 | "balanced-match": { 1054 | "version": "1.0.2", 1055 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1056 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1057 | "dev": true 1058 | }, 1059 | "binary-extensions": { 1060 | "version": "2.2.0", 1061 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1062 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1063 | "dev": true 1064 | }, 1065 | "brace-expansion": { 1066 | "version": "1.1.11", 1067 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1068 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1069 | "dev": true, 1070 | "requires": { 1071 | "balanced-match": "^1.0.0", 1072 | "concat-map": "0.0.1" 1073 | } 1074 | }, 1075 | "braces": { 1076 | "version": "3.0.2", 1077 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1078 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1079 | "dev": true, 1080 | "requires": { 1081 | "fill-range": "^7.0.1" 1082 | } 1083 | }, 1084 | "browser-stdout": { 1085 | "version": "1.3.1", 1086 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 1087 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 1088 | "dev": true 1089 | }, 1090 | "camelcase": { 1091 | "version": "6.3.0", 1092 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 1093 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 1094 | "dev": true 1095 | }, 1096 | "chai": { 1097 | "version": "4.3.4", 1098 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", 1099 | "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", 1100 | "dev": true, 1101 | "requires": { 1102 | "assertion-error": "^1.1.0", 1103 | "check-error": "^1.0.2", 1104 | "deep-eql": "^3.0.1", 1105 | "get-func-name": "^2.0.0", 1106 | "pathval": "^1.1.1", 1107 | "type-detect": "^4.0.5" 1108 | } 1109 | }, 1110 | "chalk": { 1111 | "version": "4.1.2", 1112 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1113 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1114 | "dev": true, 1115 | "requires": { 1116 | "ansi-styles": "^4.1.0", 1117 | "supports-color": "^7.1.0" 1118 | }, 1119 | "dependencies": { 1120 | "supports-color": { 1121 | "version": "7.2.0", 1122 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1123 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1124 | "dev": true, 1125 | "requires": { 1126 | "has-flag": "^4.0.0" 1127 | } 1128 | } 1129 | } 1130 | }, 1131 | "check-error": { 1132 | "version": "1.0.2", 1133 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 1134 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 1135 | "dev": true 1136 | }, 1137 | "chokidar": { 1138 | "version": "3.5.2", 1139 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", 1140 | "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", 1141 | "dev": true, 1142 | "requires": { 1143 | "anymatch": "~3.1.2", 1144 | "braces": "~3.0.2", 1145 | "fsevents": "~2.3.2", 1146 | "glob-parent": "~5.1.2", 1147 | "is-binary-path": "~2.1.0", 1148 | "is-glob": "~4.0.1", 1149 | "normalize-path": "~3.0.0", 1150 | "readdirp": "~3.6.0" 1151 | } 1152 | }, 1153 | "cliui": { 1154 | "version": "7.0.4", 1155 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1156 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1157 | "dev": true, 1158 | "requires": { 1159 | "string-width": "^4.2.0", 1160 | "strip-ansi": "^6.0.0", 1161 | "wrap-ansi": "^7.0.0" 1162 | } 1163 | }, 1164 | "color-convert": { 1165 | "version": "2.0.1", 1166 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1167 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1168 | "dev": true, 1169 | "requires": { 1170 | "color-name": "~1.1.4" 1171 | } 1172 | }, 1173 | "color-name": { 1174 | "version": "1.1.4", 1175 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1176 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1177 | "dev": true 1178 | }, 1179 | "concat-map": { 1180 | "version": "0.0.1", 1181 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1182 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 1183 | "dev": true 1184 | }, 1185 | "debug": { 1186 | "version": "4.3.2", 1187 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", 1188 | "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", 1189 | "dev": true, 1190 | "requires": { 1191 | "ms": "2.1.2" 1192 | }, 1193 | "dependencies": { 1194 | "ms": { 1195 | "version": "2.1.2", 1196 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1197 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1198 | "dev": true 1199 | } 1200 | } 1201 | }, 1202 | "decamelize": { 1203 | "version": "4.0.0", 1204 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1205 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1206 | "dev": true 1207 | }, 1208 | "deep-eql": { 1209 | "version": "3.0.1", 1210 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 1211 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 1212 | "dev": true, 1213 | "requires": { 1214 | "type-detect": "^4.0.0" 1215 | } 1216 | }, 1217 | "diff": { 1218 | "version": "5.0.0", 1219 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 1220 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 1221 | "dev": true 1222 | }, 1223 | "emoji-regex": { 1224 | "version": "8.0.0", 1225 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1226 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1227 | "dev": true 1228 | }, 1229 | "escalade": { 1230 | "version": "3.1.1", 1231 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1232 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1233 | "dev": true 1234 | }, 1235 | "escape-string-regexp": { 1236 | "version": "4.0.0", 1237 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1238 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1239 | "dev": true 1240 | }, 1241 | "fill-range": { 1242 | "version": "7.0.1", 1243 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1244 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1245 | "dev": true, 1246 | "requires": { 1247 | "to-regex-range": "^5.0.1" 1248 | } 1249 | }, 1250 | "find-up": { 1251 | "version": "5.0.0", 1252 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1253 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1254 | "dev": true, 1255 | "requires": { 1256 | "locate-path": "^6.0.0", 1257 | "path-exists": "^4.0.0" 1258 | } 1259 | }, 1260 | "flat": { 1261 | "version": "5.0.2", 1262 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1263 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1264 | "dev": true 1265 | }, 1266 | "fs.realpath": { 1267 | "version": "1.0.0", 1268 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1269 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1270 | "dev": true 1271 | }, 1272 | "fsevents": { 1273 | "version": "2.3.2", 1274 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1275 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1276 | "dev": true, 1277 | "optional": true 1278 | }, 1279 | "get-caller-file": { 1280 | "version": "2.0.5", 1281 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1282 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1283 | "dev": true 1284 | }, 1285 | "get-func-name": { 1286 | "version": "2.0.0", 1287 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 1288 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 1289 | "dev": true 1290 | }, 1291 | "glob": { 1292 | "version": "7.1.7", 1293 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", 1294 | "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", 1295 | "dev": true, 1296 | "requires": { 1297 | "fs.realpath": "^1.0.0", 1298 | "inflight": "^1.0.4", 1299 | "inherits": "2", 1300 | "minimatch": "^3.0.4", 1301 | "once": "^1.3.0", 1302 | "path-is-absolute": "^1.0.0" 1303 | } 1304 | }, 1305 | "glob-parent": { 1306 | "version": "5.1.2", 1307 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1308 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1309 | "dev": true, 1310 | "requires": { 1311 | "is-glob": "^4.0.1" 1312 | } 1313 | }, 1314 | "growl": { 1315 | "version": "1.10.5", 1316 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 1317 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 1318 | "dev": true 1319 | }, 1320 | "has-flag": { 1321 | "version": "4.0.0", 1322 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1323 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1324 | "dev": true 1325 | }, 1326 | "he": { 1327 | "version": "1.2.0", 1328 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1329 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1330 | "dev": true 1331 | }, 1332 | "inflight": { 1333 | "version": "1.0.6", 1334 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1335 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1336 | "dev": true, 1337 | "requires": { 1338 | "once": "^1.3.0", 1339 | "wrappy": "1" 1340 | } 1341 | }, 1342 | "inherits": { 1343 | "version": "2.0.4", 1344 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1345 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1346 | "dev": true 1347 | }, 1348 | "is-binary-path": { 1349 | "version": "2.1.0", 1350 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1351 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1352 | "dev": true, 1353 | "requires": { 1354 | "binary-extensions": "^2.0.0" 1355 | } 1356 | }, 1357 | "is-extglob": { 1358 | "version": "2.1.1", 1359 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1360 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 1361 | "dev": true 1362 | }, 1363 | "is-fullwidth-code-point": { 1364 | "version": "3.0.0", 1365 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1366 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1367 | "dev": true 1368 | }, 1369 | "is-glob": { 1370 | "version": "4.0.3", 1371 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1372 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1373 | "dev": true, 1374 | "requires": { 1375 | "is-extglob": "^2.1.1" 1376 | } 1377 | }, 1378 | "is-number": { 1379 | "version": "7.0.0", 1380 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1381 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1382 | "dev": true 1383 | }, 1384 | "is-plain-obj": { 1385 | "version": "2.1.0", 1386 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1387 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1388 | "dev": true 1389 | }, 1390 | "is-unicode-supported": { 1391 | "version": "0.1.0", 1392 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1393 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1394 | "dev": true 1395 | }, 1396 | "isexe": { 1397 | "version": "2.0.0", 1398 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1399 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 1400 | "dev": true 1401 | }, 1402 | "js-yaml": { 1403 | "version": "4.1.0", 1404 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1405 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1406 | "dev": true, 1407 | "requires": { 1408 | "argparse": "^2.0.1" 1409 | } 1410 | }, 1411 | "locate-path": { 1412 | "version": "6.0.0", 1413 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1414 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1415 | "dev": true, 1416 | "requires": { 1417 | "p-locate": "^5.0.0" 1418 | } 1419 | }, 1420 | "log-symbols": { 1421 | "version": "4.1.0", 1422 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1423 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1424 | "dev": true, 1425 | "requires": { 1426 | "chalk": "^4.1.0", 1427 | "is-unicode-supported": "^0.1.0" 1428 | } 1429 | }, 1430 | "minimatch": { 1431 | "version": "3.0.4", 1432 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1433 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1434 | "dev": true, 1435 | "requires": { 1436 | "brace-expansion": "^1.1.7" 1437 | } 1438 | }, 1439 | "mocha": { 1440 | "version": "9.1.4", 1441 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.4.tgz", 1442 | "integrity": "sha512-+q2aV5VlJZuLgCWoBvGI5zEwPF9eEI0kr/sAA9Jm4xMND7RfIEyF8JE7C0JIg8WXRG+P1sdIAb5ccoHPlXLzcw==", 1443 | "dev": true, 1444 | "requires": { 1445 | "@ungap/promise-all-settled": "1.1.2", 1446 | "ansi-colors": "4.1.1", 1447 | "browser-stdout": "1.3.1", 1448 | "chokidar": "3.5.2", 1449 | "debug": "4.3.2", 1450 | "diff": "5.0.0", 1451 | "escape-string-regexp": "4.0.0", 1452 | "find-up": "5.0.0", 1453 | "glob": "7.1.7", 1454 | "growl": "1.10.5", 1455 | "he": "1.2.0", 1456 | "js-yaml": "4.1.0", 1457 | "log-symbols": "4.1.0", 1458 | "minimatch": "3.0.4", 1459 | "ms": "2.1.3", 1460 | "nanoid": "3.1.25", 1461 | "serialize-javascript": "6.0.0", 1462 | "strip-json-comments": "3.1.1", 1463 | "supports-color": "8.1.1", 1464 | "which": "2.0.2", 1465 | "workerpool": "6.1.5", 1466 | "yargs": "16.2.0", 1467 | "yargs-parser": "20.2.4", 1468 | "yargs-unparser": "2.0.0" 1469 | } 1470 | }, 1471 | "ms": { 1472 | "version": "2.1.3", 1473 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1474 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1475 | "dev": true 1476 | }, 1477 | "nanoid": { 1478 | "version": "3.1.25", 1479 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", 1480 | "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", 1481 | "dev": true 1482 | }, 1483 | "normalize-path": { 1484 | "version": "3.0.0", 1485 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1486 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1487 | "dev": true 1488 | }, 1489 | "once": { 1490 | "version": "1.4.0", 1491 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1492 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1493 | "dev": true, 1494 | "requires": { 1495 | "wrappy": "1" 1496 | } 1497 | }, 1498 | "p-limit": { 1499 | "version": "3.1.0", 1500 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1501 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1502 | "dev": true, 1503 | "requires": { 1504 | "yocto-queue": "^0.1.0" 1505 | } 1506 | }, 1507 | "p-locate": { 1508 | "version": "5.0.0", 1509 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1510 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1511 | "dev": true, 1512 | "requires": { 1513 | "p-limit": "^3.0.2" 1514 | } 1515 | }, 1516 | "path-exists": { 1517 | "version": "4.0.0", 1518 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1519 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1520 | "dev": true 1521 | }, 1522 | "path-is-absolute": { 1523 | "version": "1.0.1", 1524 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1525 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1526 | "dev": true 1527 | }, 1528 | "pathval": { 1529 | "version": "1.1.1", 1530 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", 1531 | "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", 1532 | "dev": true 1533 | }, 1534 | "picomatch": { 1535 | "version": "2.3.1", 1536 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1537 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1538 | "dev": true 1539 | }, 1540 | "randombytes": { 1541 | "version": "2.1.0", 1542 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1543 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1544 | "dev": true, 1545 | "requires": { 1546 | "safe-buffer": "^5.1.0" 1547 | } 1548 | }, 1549 | "readdirp": { 1550 | "version": "3.6.0", 1551 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1552 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1553 | "dev": true, 1554 | "requires": { 1555 | "picomatch": "^2.2.1" 1556 | } 1557 | }, 1558 | "require-directory": { 1559 | "version": "2.1.1", 1560 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1561 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1562 | "dev": true 1563 | }, 1564 | "safe-buffer": { 1565 | "version": "5.2.1", 1566 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1567 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1568 | "dev": true 1569 | }, 1570 | "serialize-javascript": { 1571 | "version": "6.0.0", 1572 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1573 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1574 | "dev": true, 1575 | "requires": { 1576 | "randombytes": "^2.1.0" 1577 | } 1578 | }, 1579 | "string-width": { 1580 | "version": "4.2.3", 1581 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1582 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1583 | "dev": true, 1584 | "requires": { 1585 | "emoji-regex": "^8.0.0", 1586 | "is-fullwidth-code-point": "^3.0.0", 1587 | "strip-ansi": "^6.0.1" 1588 | } 1589 | }, 1590 | "strip-ansi": { 1591 | "version": "6.0.1", 1592 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1593 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1594 | "dev": true, 1595 | "requires": { 1596 | "ansi-regex": "^5.0.1" 1597 | } 1598 | }, 1599 | "strip-json-comments": { 1600 | "version": "3.1.1", 1601 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1602 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1603 | "dev": true 1604 | }, 1605 | "supports-color": { 1606 | "version": "8.1.1", 1607 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1608 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1609 | "dev": true, 1610 | "requires": { 1611 | "has-flag": "^4.0.0" 1612 | } 1613 | }, 1614 | "to-regex-range": { 1615 | "version": "5.0.1", 1616 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1617 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1618 | "dev": true, 1619 | "requires": { 1620 | "is-number": "^7.0.0" 1621 | } 1622 | }, 1623 | "type-detect": { 1624 | "version": "4.0.8", 1625 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1626 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 1627 | "dev": true 1628 | }, 1629 | "which": { 1630 | "version": "2.0.2", 1631 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1632 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1633 | "dev": true, 1634 | "requires": { 1635 | "isexe": "^2.0.0" 1636 | } 1637 | }, 1638 | "workerpool": { 1639 | "version": "6.1.5", 1640 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", 1641 | "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", 1642 | "dev": true 1643 | }, 1644 | "wrap-ansi": { 1645 | "version": "7.0.0", 1646 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1647 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1648 | "dev": true, 1649 | "requires": { 1650 | "ansi-styles": "^4.0.0", 1651 | "string-width": "^4.1.0", 1652 | "strip-ansi": "^6.0.0" 1653 | } 1654 | }, 1655 | "wrappy": { 1656 | "version": "1.0.2", 1657 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1658 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1659 | "dev": true 1660 | }, 1661 | "y18n": { 1662 | "version": "5.0.8", 1663 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1664 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1665 | "dev": true 1666 | }, 1667 | "yargs": { 1668 | "version": "16.2.0", 1669 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1670 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1671 | "dev": true, 1672 | "requires": { 1673 | "cliui": "^7.0.2", 1674 | "escalade": "^3.1.1", 1675 | "get-caller-file": "^2.0.5", 1676 | "require-directory": "^2.1.1", 1677 | "string-width": "^4.2.0", 1678 | "y18n": "^5.0.5", 1679 | "yargs-parser": "^20.2.2" 1680 | } 1681 | }, 1682 | "yargs-parser": { 1683 | "version": "20.2.4", 1684 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1685 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1686 | "dev": true 1687 | }, 1688 | "yargs-unparser": { 1689 | "version": "2.0.0", 1690 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1691 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1692 | "dev": true, 1693 | "requires": { 1694 | "camelcase": "^6.0.0", 1695 | "decamelize": "^4.0.0", 1696 | "flat": "^5.0.2", 1697 | "is-plain-obj": "^2.1.0" 1698 | } 1699 | }, 1700 | "yocto-queue": { 1701 | "version": "0.1.0", 1702 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1703 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1704 | "dev": true 1705 | } 1706 | } 1707 | } 1708 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@codewars/lambda-calculus", 3 | "type": "module", 4 | "version": "1.1.0", 5 | "description": "Lambda Calculus evaluator for Codewars", 6 | "main": "src/lambda-calculus.js", 7 | "files": [ 8 | "src/" 9 | ], 10 | "scripts": { 11 | "test": "mocha --reporter spec ./tests/*/test.js", 12 | "repl": "node ./src/LC-REPL.js", 13 | "postversion": "git push && git push --tags" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/codewars/lambda-calculus.git" 18 | }, 19 | "keywords": [ 20 | "lc", 21 | "lambda-calculus" 22 | ], 23 | "author": "Kacarott", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/codewars/lambda-calculus/issues" 27 | }, 28 | "homepage": "https://github.com/codewars/lambda-calculus#readme", 29 | "devDependencies": { 30 | "chai": "^4.3.4", 31 | "mocha": "^9.1.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/LC-REPL.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Simple custom REPL for testing LC code. 4 | 5 | Written by Kacarott 6 | */ 7 | 8 | import repl from "repl"; 9 | import * as LC from "./lambda-calculus.js"; 10 | 11 | // REPL defaults 12 | LC.config.purity = "LetRec"; 13 | LC.config.numEncoding = "Church"; 14 | LC.config.verbosity = "Calm"; 15 | 16 | // State 17 | const namespace = {}; 18 | 19 | // For prepending all predefined terms to new definitions 20 | function prep(text) { 21 | for (const [name, term] of Object.entries(namespace).reverse()) { 22 | if (["_cache", "trace"].includes(name)) continue; 23 | text = `${name} = ${String(term.term)}\n${text}`; 24 | } 25 | return text; 26 | } 27 | 28 | // Evaluate a block of input 29 | function acceptInput(userInput, context, filename, finish) { 30 | if (/^\s*$/.test(userInput)) return finish(); 31 | if (!/=/.test(userInput)) { // evaluation, not assignment 32 | const {result} = LC.compile(prep("result = " + userInput)); 33 | console.log(String(result.term)) 34 | return finish(); 35 | } 36 | const result = LC.compile(prep(userInput)); 37 | Object.assign(namespace, result); 38 | return finish(); 39 | } 40 | 41 | // Repl instance 42 | repl.start({ prompt: "λ: ", eval: acceptInput}).context = {}; 43 | -------------------------------------------------------------------------------- /src/lambda-calculus.js: -------------------------------------------------------------------------------- 1 | /* 2 | Lambda Calculus evaluator supporting: 3 | - unlimited recursion 4 | - call by need 5 | - fast (ish?) evaluation 6 | - shortform syntax 7 | 8 | And additional features: 9 | - debugging aids 10 | - predefined encodings 11 | - purity modes 12 | 13 | Written by 14 | JohanWiltink - https://github.com/JohanWiltink 15 | Kacarott - https://github.com/Kacarott 16 | */ 17 | 18 | // Default options 19 | const config = { verbosity: "Calm" // Calm | Concise | Loquacious | Verbose 20 | , purity: "PureLC" // Let | LetRec | PureLC 21 | , numEncoding: "None" // None | Church | Scott | BinaryScott 22 | , enforceBinaryScottInvariant: true // Boolean 23 | }; 24 | 25 | export function configure(cfg={}) { return Object.assign(config,cfg); } 26 | 27 | function union(left, right) { 28 | const r = new Set(left); 29 | for ( const name of right ) r.add(name); 30 | return r; 31 | } 32 | 33 | class V { 34 | constructor(name) { this.name = name; } 35 | free() { return new Set([ this.name ]); } 36 | toString() { return this.name.toString(); } 37 | } 38 | 39 | class L { 40 | constructor(name, body) { 41 | this.name = name; 42 | this.body = body; 43 | } 44 | free() { 45 | const r = this.body?.free?.() || new Set ; 46 | r.delete(this.name); 47 | return r; 48 | } 49 | toString() { 50 | return this.body instanceof L 51 | ? `\\ ${this.name}${this.body.toString().slice(1)}` 52 | : `\\ ${this.name} . ${this.body.toString()}`; 53 | } 54 | } 55 | 56 | class A { 57 | constructor(left, right) { 58 | this.left = left; 59 | this.right = right; 60 | } 61 | free() { return union( this.left?.free?.() || [] , this.right?.free?.() || [] ); } 62 | toString() { 63 | const left = this.left instanceof L ? `(${this.left})` : this.left ; 64 | const right = this.right instanceof V ? this.right : `(${this.right})` ; 65 | return `${left} ${right}`; 66 | } 67 | } 68 | 69 | class Env extends Map { 70 | // use inherited set, get for copying references 71 | // insert and retrieve values with setThunk and getValue 72 | // encoding of value is a Generator Object that yields value forever - this is opaque 73 | setThunk(i,val) { 74 | this.set(i, function*() { 75 | // console.warn(`expensively calculating ${ i }`); 76 | const result = (yield val) ?? val; // If val is not A or V, then it need not be evaluated 77 | while ( true ) yield result; 78 | } () ); 79 | return this; 80 | } 81 | // Second argument provides an interface for storing the evaluated term inside 82 | // the generator, for future accesses. 83 | getValue(i, result) { 84 | // console.warn(`inexpensively fetching ${ i }`); 85 | return this.get(i).next(result).value; 86 | } 87 | } 88 | 89 | // Term and Env pair, used internally to keep track of current computation in eval 90 | class Tuple { 91 | constructor(term,env) { Object.assign(this,{term,env}); } 92 | valueOf() { return toInt(this.term); } 93 | toString() { return this.term.toString(); } 94 | } 95 | 96 | // Used to insert an external (JS) value into evaluation manually ( avoiding implicit number conversion ) 97 | export function Primitive(v) { return new Tuple(new V( "" ), new Env([[ "" , function*() { while ( true ) yield v; } () ]])); } 98 | 99 | const primitives = new Env; 100 | primitives.setThunk( "trace", new Tuple( Primitive( function(v) { console.info(String(v.term)); return v; } ), new Env ) ); 101 | 102 | const Y = new L("f",new A(new L("x",new A(new V("f"),new A(new V("x"),new V("x")))),new L("x",new A(new V("f"),new A(new V("x"),new V("x")))))); 103 | 104 | export function fromInt(n) { 105 | if ( config.numEncoding === "Church" ) 106 | if ( n >= 0 ) 107 | return new L("s", new L("z", Array(n).fill().reduce( s => new A(new V("s"), s), new V("z")) )); 108 | else { 109 | if ( config.verbosity >= "Concise" ) console.error(`fromInt.Church: negative number ${ n }`); 110 | throw new RangeError; 111 | } 112 | else if ( config.numEncoding === "Scott" ) // data Int = Zero | Succ Int 113 | if ( n >= 0 ) 114 | return new Array(n).fill().reduce( v => new L('_', new L('f', new A(new V('f'), v))), new L('z', new L('_', new V('z'))) ); 115 | else { 116 | if ( config.verbosity >= "Concise" ) console.error(`fromInt.Scott: negative number ${ n }`); 117 | throw new RangeError; 118 | } 119 | else if ( config.numEncoding === "BinaryScott" ) // data Int = End | Even Int | Odd Int // LittleEndian, padding ( trailing ) 0 bits are out of spec - behaviour is undefined 120 | if ( n >= 0 ) { 121 | const zero = new L('z', new L('_', new L('_', new V('z')))); 122 | return n ? n.toString(2).split("").reduce( (z,bit) => new L('_', new L('f', new L('t', new A(new V( bit==='0' ? 'f' : 't' ), z)))), zero ) : zero ; 123 | } else { 124 | if ( config.verbosity >= "Concise" ) console.error(`fromInt.BinaryScott: negative number ${ n }`); 125 | throw new RangeError; 126 | } 127 | else if ( config.numEncoding === "None" ) { 128 | if ( config.verbosity >= "Concise" ) console.error(`fromInt.None: number ${ n }`); 129 | throw new EvalError; 130 | } else 131 | return config.numEncoding.fromInt(n); // Custom encoding 132 | } 133 | 134 | const isPadded = n => n (false) ( z => z (true) ( () => isPadded(z) ) ( () => isPadded(z) ) ) (isPadded) ; // check invariant for BinaryScott number 135 | 136 | export function toInt(term) { 137 | try { 138 | if ( config.numEncoding === "Church" ) { 139 | const succ = x => x+1 ; 140 | const result = term ( succ ) ; 141 | return result ( result === succ ? 0 : Primitive(0) ); 142 | } else if ( config.numEncoding === "Scott" ) { 143 | let result = 0, evaluating = true; 144 | while ( evaluating ) 145 | term ( () => evaluating = false ) ( n => () => { term = n; result++ } ) (); 146 | return result; 147 | } else if ( config.numEncoding === "BinaryScott" ) { 148 | const throwOnPadding = config.enforceBinaryScottInvariant && isPadded(term); 149 | let result = 0, bit = 1, evaluating = true; 150 | while ( evaluating ) 151 | term ( () => evaluating = false ) ( n => () => { term = n; bit *= 2 } ) ( n => () => { term = n; result += bit; bit *= 2 } ) (); 152 | if ( throwOnPadding ) { 153 | if ( config.verbosity >= "Concise" ) console.error(`toInt: Binary Scott number ${ result } has zero-padding`); 154 | throw new TypeError(`toInt: Binary Scott number violates invariant`); 155 | } 156 | return result; 157 | } else if ( config.numEncoding === "None" ) 158 | return term; 159 | else 160 | return config.numEncoding.toInt(term); // Custom encoding 161 | } catch (e) { 162 | if ( ! e.message.startsWith("toInt:") && config.verbosity >= "Concise" ) 163 | console.error(`toInt: ${ term } is not a number in numEncoding ${ config.numEncoding }`); 164 | throw e; 165 | } 166 | } 167 | 168 | // parse :: String -> Env { String => Term } 169 | function parse(code) { 170 | function parseTerm(env,code) { 171 | function wrap(name,term) { 172 | const FV = term?.free?.() || new Set ; FV.delete("()"); 173 | if ( config.purity === "Let" ) 174 | return Array.from(FV).reduce( (tm,nm) => { 175 | if ( env.has(nm) ) { 176 | if ( config.verbosity >= "Verbose" ) console.debug(` using ${ nm } = ${ env.getValue(nm) }`); 177 | tm.env.set( nm, env.get(nm) ); 178 | return tm; 179 | } else { 180 | if ( config.verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); 181 | if ( nm === name ) 182 | throw new ReferenceError(`undefined free variable ${ nm }: direct recursive calls are not supported in Let mode`); 183 | else 184 | throw new ReferenceError(`undefined free variable ${ nm }`); 185 | } 186 | } , new Tuple( term, new Env ) ); 187 | else if ( config.purity==="LetRec" ) 188 | return Array.from(FV).reduce( (tm,nm) => { 189 | if ( nm === name ) 190 | return tm; 191 | else if ( env.has(nm) ) { 192 | if ( config.verbosity >= "Verbose" ) console.debug(` using ${ nm } = ${ env.getValue(nm) }`); 193 | tm.env.set( nm, env.get(nm) ); 194 | return tm; 195 | } else { 196 | if ( config.verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); 197 | throw new ReferenceError(`undefined free variable ${ nm }`); 198 | } 199 | } , new Tuple( FV.has(name) ? new A(Y,new L(name,term)) : term , new Env ) ); 200 | else if ( config.purity==="PureLC" ) 201 | if ( FV.size ) { 202 | if ( config.verbosity >= "Concise" ) console.error(`parse: while defining ${ name } = ${ term }`); 203 | throw new EvalError(`unresolvable free variable(s) ${ Array.from(FV) }: all expressions must be closed in PureLC mode`); 204 | } else 205 | return new Tuple( term, new Env ); 206 | else 207 | throw new RangeError(`config.purity: unknown setting "${ purity }"`); 208 | } 209 | const letters = /[a-z]/i; 210 | const digits = /\d/; 211 | const identifierChars = /[-'\w]/; 212 | const whitespace = /\s/; 213 | function error(i,msg) { 214 | console.error(code); 215 | console.error(' '.repeat(i) + '^'); 216 | console.error(msg + " at position " + i); 217 | throw new SyntaxError(msg); 218 | } 219 | function sp(i) { while ( whitespace.test( code[i] || "" ) ) i++; return i; } 220 | const expect = c => function(i) { return code[i]===c ? sp(i+1) : 0 ; } ; 221 | function name(i) { 222 | if ( code[i]==='_' ) { 223 | while ( identifierChars.test( code[i] || "" ) ) i++; 224 | if ( code[i]==='?' ) i++; 225 | return [sp(i),"_"]; 226 | } else if ( letters.test( code[i] || "" ) ) { 227 | let j = i+1; 228 | while ( identifierChars.test( code[j] || "" ) ) j++; 229 | if ( code[j]==='?' ) j++; 230 | return [sp(j),code.slice(i,j)]; 231 | } else 232 | return null; 233 | } 234 | function n(i) { 235 | if ( digits.test(code[i]) ) { 236 | let j = i+1; 237 | while ( digits.test(code[j]) ) j++; 238 | return [ sp(j), fromInt(Number(code.slice(i,j))), Number(code.slice(i,j)) ]; // return JS number in addition to LC number // negative uses this 239 | } else 240 | return null; 241 | } 242 | function v(i) { 243 | const r = name(i); 244 | if ( r ) { 245 | const [j,termName] = r; 246 | if ( termName==="_" ) { 247 | const undef = new V("()"); 248 | undef.defName = name(0)[1]; 249 | return [j,undef]; 250 | } else 251 | return [j,new V(termName)]; 252 | } else 253 | return null; 254 | } 255 | function l(i) { 256 | const j = expect('\\')(i); 257 | if ( j ) { 258 | let arg, args = []; 259 | let k = j; 260 | while ( arg = name(k) ) { 261 | [k,arg] = arg; 262 | args.push(arg); 263 | } 264 | if ( args.length ) { 265 | const l = expect('.')(k) || error(k,"lambda: expected '.'") ; 266 | const [m,body] = term(l) || error(l,"lambda: expected a lambda calculus term") ; 267 | return [ m, args.reduceRight( (body,arg) => new L(arg,body) , body ) ]; 268 | } else 269 | error(k,"lambda: expected at least one named argument"); 270 | } else 271 | return null; 272 | } 273 | function a(i) { 274 | let q, r = []; 275 | let j = i; 276 | while ( q = paren_d(term)(j) || l(j) || v(j) || n(j) || negative(n)(j) ) { 277 | [j,q] = q; 278 | r.push(q); 279 | } 280 | if ( r.length ) 281 | return [ j, r.reduce( (z,term) => new A(z,term) ) ]; 282 | else 283 | return null; 284 | } 285 | const negative = number => function(i) { 286 | const j = expect('-')(i); 287 | if ( j ) { 288 | const q = number(j); 289 | if ( q ) { 290 | const [k,,r] = q; // act on the JS number instead of the LC number 291 | return [k,fromInt(-r)]; 292 | } else 293 | error(j,"negative: expected a number literal") 294 | } else 295 | return null; 296 | } 297 | const paren_d = inner => function(i) { 298 | const j = expect('(')(i); 299 | if ( j ) { 300 | const q = inner(j); 301 | if ( q ) { 302 | const [k,r] = q; 303 | const l = expect(')')(k) || error(k,"paren_d: expected ')'") ; 304 | return [l,r]; 305 | } else { 306 | const k = expect(')')(j) || error(j,"paren_d: expected ')'") ; 307 | const undefinedTerm = new V("()"); 308 | undefinedTerm.defName = name(0)[1]; 309 | return [k, undefinedTerm]; 310 | } 311 | } else 312 | return null; 313 | } ; 314 | const term = a; 315 | function defn(i) { 316 | const [j,nm] = name(i) || error(i,"defn: expected a name") ; 317 | const k = expect('=')(j) || error(j,"defn: expected '='") ; 318 | const [l,tm] = term(k) || error(k,"defn: expected a lambda calculus term") ; 319 | return [l,[nm,tm]]; 320 | } 321 | const [i,r] = defn(0); 322 | if ( i===code.length ) { 323 | const [name,term] = r; 324 | if ( config.verbosity >= "Loquacious" ) 325 | console.debug(`compiled ${ name }${ config.verbosity >= "Verbose" ? ` = ${ term }` : "" }`); 326 | return env.setThunk( name, wrap(name,term)); 327 | } else 328 | error(i,"defn: incomplete parse"); 329 | } 330 | return code.replace( /#.*$/gm, "" ) // ignore comments 331 | .replace( /\n(?=\s)/g, "" ) // continue lines 332 | .split( '\n' ) // split lines 333 | .filter( term => /\S/.test(term) ) // skip empty lines 334 | .reduce(parseTerm, new Env(primitives)); // parse lines 335 | } 336 | 337 | export function compile(code) { 338 | if ( typeof code !== "string" || ! code ) throw new TypeError("missing code"); 339 | const env = parse(code); 340 | const r = {}; 341 | for ( const [name] of env ) 342 | Object.defineProperty( r, name, { get() { return evalLC(new Tuple(new V(name), env)); }, enumerable: true } ); 343 | return r; 344 | } 345 | 346 | // Top level call, term :: Tuple 347 | function evalLC(term) { 348 | 349 | // builds function to return to user ( representing an abstraction awaiting input ) 350 | function awaitArg(term, env) { 351 | // callback function which will evaluate term.body in an env with the input 352 | function result(arg) { 353 | let argEnv; 354 | if ( arg?.term && arg?.env ) ({ term: arg, env: argEnv } = arg); // if callback is passed another callback, or a term 355 | const termVal = new Tuple( typeof arg === 'number' ? fromInt(arg) : arg , new Env(argEnv) ); 356 | if ( term.name==="_" ) 357 | return runEval( new Tuple(term.body, new Env(env)) ); 358 | else 359 | return runEval( new Tuple(term.body, new Env(env).setThunk(term.name, termVal)) ); 360 | } 361 | return Object.assign( result, {term,env} ); 362 | } 363 | 364 | // term :: Tuple 365 | // isRight :: bool (indicating whether the term is left or right side of an Application) 366 | // isEvaluated :: bool (indicating whether the current term should be stored in the Env) 367 | // callstack :: [String] (History of var lookups, for better error messages) 368 | function runEval({term,env}) { // stack: [[term, isRight, isEvaluated]], term: LC term, env = Env { name => term } 369 | const callstack = [], stack = []; // Does not persist between requests for arguments 370 | while ( ! ( term instanceof L ) || stack.length > 0 ) { 371 | if ( term instanceof V ) 372 | if ( term.name==="()" ) 373 | { printStackTrace("eval: evaluating undefined", term, callstack); throw new EvalError; } 374 | else { 375 | callstack.push(term.name); 376 | const res = env.getValue(term.name); 377 | if ( ! res.env ) 378 | term = res; 379 | else { 380 | if (res.term instanceof V || res.term instanceof A) 381 | // Push a frame to the stack to indicate when the value should be stored back 382 | stack.push( [new Tuple( term, env ), true, true ] ); 383 | ({term, env} = res); 384 | } 385 | } 386 | else if ( term instanceof A ) { 387 | stack.push([ new Tuple(term.right, new Env(env)), true ]); 388 | term = term.left; 389 | } else if ( term instanceof L ) { 390 | const [ { term: lastTerm, env: lastEnv }, isRight, isEvaluated ] = stack.pop(); 391 | if ( isEvaluated ) { 392 | // A non-evaluated term was received from an Env, but it is now evaluated. 393 | // Store it. 394 | lastEnv.getValue(lastTerm.name, new Tuple(term, env)); 395 | } else if ( isRight ) { 396 | if ( term.name !== "_" ) 397 | env = new Env(env).setThunk(term.name, new Tuple(lastTerm, lastEnv)); 398 | term = term.body; 399 | } else { // Pass the function some other function. 400 | term = lastTerm(awaitArg(term, env)); 401 | } 402 | } else if ( term instanceof Tuple ) { 403 | // for primitives 404 | ({term, env} = term); 405 | } else { // Not a term 406 | if ( stack.length === 0 ) return term; 407 | const [ { term: lastTerm, env: lastEnv }, isRight, isEvaluated ] = stack.pop(); 408 | if ( isEvaluated ) { 409 | // A non-evaluated term was received from an Env, but it is now evaluated. 410 | // Store it. 411 | lastEnv.getValue(lastTerm.name, new Tuple(term, env)); 412 | } else if ( isRight ) { 413 | stack.push([ new Tuple(term, new Env(env)), false ]); 414 | term = lastTerm; 415 | env = lastEnv; 416 | } else { // lastTerm is a JS function 417 | const res = lastTerm(term); 418 | if ( res?.term ) 419 | ( {term, env=new Env} = res ); 420 | else 421 | term = res; 422 | } 423 | } 424 | } 425 | // We need input 426 | return awaitArg(term, env); 427 | } 428 | return runEval(term); 429 | } 430 | 431 | // Print an error, with stack trace according to verbosity level 432 | function printStackTrace(error, term, stack) { 433 | if ( config.verbosity >= "Concise" ) 434 | console.error(`${ error } inside definition of ${ term.defName }`); 435 | 436 | const stackCutoff = config.verbosity < "Verbose" && stack[stack.length-1] == term.defName ? stack.indexOf(term.defName) + 1 : 0 ; 437 | 438 | if ( config.verbosity >= "Loquacious" ) 439 | console.error( stack.slice(stackCutoff).reverse().map( v => `\twhile evaluating ${ v }`).join('\n') ); 440 | } 441 | 442 | Object.defineProperty( Function.prototype, "valueOf", { value: function valueOf() { if (this.term) return toInt(this); else return this; } } ); 443 | -------------------------------------------------------------------------------- /tests/basics-binary-scott/solution.txt: -------------------------------------------------------------------------------- 1 | # BinaryScott Number 2 | 3 | #import Combinators.lc 4 | 5 | B = \ f g x . f (g x) 6 | C = \ f x y . f y x 7 | I = \ x . x 8 | K = \ x _ . x 9 | KI = \ _ x . x 10 | M = \ x . x x 11 | S = \ f g x . f x (g x) 12 | T = \ x f . f x 13 | V = \ x y f . f x y 14 | W = \ f x . f x x 15 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 16 | Z = \ f . ( \ x . f \ y . x x y ) ( \ x . f \ y . x x y ) 17 | 18 | #import Boolean.lc 19 | 20 | False = \ f _t . f 21 | True = \ _f t . t 22 | 23 | not = \ p . \ f t . p t f 24 | 25 | and = \ p . p p 26 | or = \ p q . p q p 27 | xor = \ p q . p q (not q) 28 | implies = \ p . p True 29 | 30 | #import Ordering.lc 31 | 32 | LT = \ lt _eq _gt . lt 33 | EQ = \ _lt eq _gt . eq 34 | GT = \ _lt _eq gt . gt 35 | 36 | #import Pair.lc 37 | 38 | Pair = \ x y fn . fn x y 39 | 40 | fst = \ xy . xy \ x _y . x 41 | snd = \ xy . xy \ _x y . y 42 | 43 | first = \ fn xy . xy \ x . Pair (fn x) 44 | second = \ fn xy . xy \ x y . Pair x (fn y) 45 | both = \ fn xy . xy \ x y . Pair (fn x) (fn y) 46 | bimap = \ f g xy . xy \ x y . Pair (f x) (g y) 47 | curry = \ fn xy . xy fn 48 | 49 | # data Number = End | Even Number | Odd Number 50 | 51 | # zero = \ end _even _odd . end 52 | 53 | shiftR0 = \ n . \ _end even _odd . even n # mind that a shiftR in LE is a multiplication 54 | shiftR1 = \ n . \ _end _even odd . odd n # mind that a shiftR in LE is a multiplication 55 | shiftL = \ n . n 0 I I # mind that a shiftL in LE is a division 56 | 57 | dbl = \ n . n 0 (K (shiftR0 n)) (K (shiftR0 n)) 58 | 59 | isStrictZero? = \ n . n True (K False) (K False) # disallow padding zeroes # O(1) 60 | isZero? = \ n . n True isZero? (K False) # allow padding zeroes # amortised O(2), so don't worry too much 61 | 62 | pad = \ n . n (shiftR0 0) (B shiftR0 pad) (B shiftR1 pad) 63 | unpad = \ n . n 0 ( \ z . ( \ unpadZ . isStrictZero? unpadZ (shiftR0 unpadZ) 0 ) (unpad z) ) (B shiftR1 unpad) 64 | isPadded? = \ n . n False ( \ z . z True (K (isPadded? z)) (K (isPadded? z)) ) isPadded? 65 | 66 | # instance Ord 67 | 68 | # will break horribly on padded numbers 69 | compare = \ m n . m (n EQ (K LT) (K LT)) 70 | ( \ zm . n GT (compare zm) ( \ zn . compare zm zn LT LT GT ) ) 71 | ( \ zm . n GT ( \ zn . compare zm zn LT GT GT ) (compare zm) ) 72 | lt = \ m n . compare m n True False False 73 | le = \ m n . compare m n True True False 74 | eq = \ m n . compare m n False True False 75 | ge = \ m n . compare m n False True True 76 | gt = \ m n . compare m n False False True 77 | 78 | # instance Enum 79 | 80 | succ = \ n . n 1 shiftR1 (B shiftR0 succ) 81 | 82 | go = \ prefix n . n 0 83 | (go (B prefix shiftR1)) 84 | ( \ z . z (prefix z) (K (prefix (shiftR0 z))) (K (prefix (shiftR0 z))) ) 85 | pred = go I # allow padding zeroes 86 | pred = \ n . n 0 (B shiftR1 pred) dbl # disallow padding zeroes 87 | 88 | # instance Bits 89 | 90 | bitAnd = \ m n . m 0 91 | ( \ zm . n 0 (B dbl (bitAnd zm)) (B dbl (bitAnd zm)) ) 92 | ( \ zm . n 0 (B dbl (bitAnd zm)) (B shiftR1 (bitAnd zm)) ) 93 | 94 | bitOr = \ m n . m n 95 | ( \ zm . n (shiftR0 zm) (B shiftR0 (bitOr zm)) (B shiftR1 (bitOr zm)) ) 96 | ( \ zm . n (shiftR1 zm) (B shiftR1 (bitOr zm)) (B shiftR1 (bitOr zm)) ) 97 | 98 | bitXor = \ m n . m n 99 | ( \ zm . n ( dbl zm) (B dbl (bitXor zm)) (B shiftR1 (bitXor zm)) ) 100 | ( \ zm . n (shiftR1 zm) (B shiftR1 (bitXor zm)) (B dbl (bitXor zm)) ) 101 | 102 | testBit = \ i n . isZero? i 103 | (n False (testBit (pred i)) (testBit (pred i))) 104 | (n False (K False) (K True)) 105 | 106 | bit = \ i . isZero? i (shiftR0 (bit (pred i))) (succ i) 107 | 108 | popCount = \ n . n 0 popCount (B succ popCount) 109 | 110 | even = \ n . n True (K True) (K False) 111 | odd = \ n . n False (K False) (K True) 112 | 113 | # instance Num 114 | 115 | plus = \ m n . m n 116 | ( \ zm . n (shiftR0 zm) (B shiftR0 (plus zm)) (B shiftR1 (plus zm)) ) 117 | ( \ zm . n (shiftR1 zm) (B shiftR1 (plus zm)) (B shiftR0 (B succ (plus zm))) ) 118 | 119 | times = \ m n . m 0 120 | ( \ zm . n 0 121 | ( \ zn . shiftR0 (shiftR0 (times zm zn)) ) 122 | ( \ zn . shiftR0 (times zm (shiftR1 zn)) ) 123 | ) 124 | ( \ zm . n 0 125 | ( \ zn . shiftR0 (times (shiftR1 zm) zn) ) 126 | ( \ zn . plus (shiftR1 zn) (shiftR0 (times zm (shiftR1 zn))) ) 127 | ) 128 | 129 | unsafeMinus = \ m n . m 0 130 | ( \ zm . n (shiftR0 zm) (B shiftR0 (unsafeMinus zm)) (B shiftR1 (B pred (unsafeMinus zm))) ) 131 | ( \ zm . n (shiftR1 zm) (B shiftR1 (unsafeMinus zm)) (B shiftR0 (unsafeMinus zm)) ) 132 | # needs explicit unpad or will litter padding 133 | minus = \ m n . gt m n 0 (unpad (unsafeMinus m n)) 134 | # this should solve the littering 135 | go = \ m n . m 0 136 | ( \ zm . n m (B dbl (go zm)) (B shiftR1 (B pred (go zm))) ) 137 | ( \ zm . n m (B shiftR1 (go zm)) (B dbl (go zm)) ) 138 | minus = \ m n . gt m n 0 (go m n) 139 | 140 | until = \ p fn x . p x (until p fn (fn x)) x 141 | divMod = \ m n . until (B (lt m) snd) (bimap succ shiftR0) (Pair 0 n) 142 | \ steps nn . isZero? steps 143 | (divMod (minus m (shiftL nn)) n (B Pair (plus (bit (pred steps))))) 144 | (Pair 0 m) 145 | div = \ m n . fst (divMod m n) 146 | mod = \ m n . snd (divMod m n) 147 | 148 | square = W times 149 | pow = \ m n . n 1 150 | (B square (pow m)) 151 | (B (times m) (B square (pow m))) 152 | 153 | gcd = \ m n . m n 154 | ( \ zm . n m (B shiftR0 (gcd zm)) (K (gcd zm n)) ) 155 | ( \ _ . n m 156 | (gcd m) 157 | ( \ _ . compare m n 158 | (gcd (minus n m) m) 159 | m 160 | (gcd (minus m n) n) 161 | ) 162 | ) 163 | lcm = \ m n . T (gcd m n) \ g . isZero? g (times (div m g) n) g 164 | 165 | min = \ m n . le m n n m 166 | max = \ m n . le m n m n 167 | -------------------------------------------------------------------------------- /tests/basics-binary-scott/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert} from "chai"; 3 | 4 | import * as LC from "../../src/lambda-calculus.js"; 5 | LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); 6 | 7 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 8 | const solution = LC.compile(solutionText); 9 | const {fromInt,toInt} = LC; 10 | 11 | const {False,True,not,and,or,xor,implies} = solution; 12 | const {LT,EQ,GT,compare,lt,le,eq,ge,gt} = solution; 13 | const {Pair,fst,snd,first,second,both,bimap,curry} = solution; 14 | const {shiftR0,shiftR1,shiftL,dbl,isStrictZero,isZero,pad,unpad,"isPadded?":isPadded} = solution; 15 | const {succ,pred} = solution; 16 | const {bitAnd,bitOr,bitXor,testBit,bit,popCount,even,odd} = solution; 17 | const {plus,times,minus,divMod,div,mod,pow,gcd,lcm,min,max} = solution; 18 | 19 | const toString = n => n ( "$" ) ( z => '0' + toString(z) ) ( z => '1' + toString(z) ) ; 20 | const toPair = xy => [ fst(xy), snd(xy) ] ; 21 | 22 | const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; 23 | const refGCD = m => n => n ? refGCD(n)(m%n) : m ; 24 | 25 | describe("Binary Scott tests",function(){ 26 | this.timeout(0); 27 | it("enumeration",()=>{ 28 | LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); 29 | const zero = end => _odd => _even => end ; 30 | const one = succ(zero); 31 | const two = succ(one); 32 | const three = succ(two); 33 | const four = succ(three); 34 | const five = succ(four); 35 | assert.equal( toString(zero), "$" ); 36 | assert.equal( toString(one), "1$" ); 37 | assert.equal( toString(two), "01$" ); 38 | assert.equal( toString(three), "11$" ); 39 | assert.equal( toString(four), "001$" ); 40 | assert.equal( toString(five), "101$" ); 41 | assert.equal( toString(five), "101$" ); 42 | assert.equal( toString(pred(five)), "001$" ); 43 | assert.equal( toString(pred(pred(five))), "11$" ); 44 | assert.equal( toString(pred(pred(pred(five)))), "01$" ); 45 | assert.equal( toString(pred(pred(pred(pred(five))))), "1$" ); 46 | assert.equal( toString(pred(pred(pred(pred(pred(five)))))), "$" ); 47 | }); 48 | it("successor",()=>{ 49 | let n = 0; 50 | for ( let i=1; i<=100; i++ ) { 51 | n = succ (n); 52 | assert.equal( n, i ); 53 | } 54 | }); 55 | it("predecessor",()=>{ 56 | let n = 100; 57 | for ( let i=100; i--; ) { 58 | n = pred (n); 59 | assert.equal( n, i ); 60 | } 61 | }); 62 | // enforcing the invariant means pred robustness is overrated 63 | // it("predecessor robustness",()=>{ 64 | // assert.equal( toString( pred ( 2 ) ), "1$" ); 65 | // assert.equal( toString( pred ( end => even => odd => end ) ), "$" ); 66 | // assert.equal( toString( pred ( end => even => odd => even ( 67 | // end => even => odd => end ) ) ), "$" ); 68 | // assert.equal( toString( pred ( end => even => odd => even ( 69 | // end => even => odd => even ( 70 | // end => even => odd => end ) ) ) ), "$" ); 71 | // }); 72 | it("ordering",()=>{ 73 | for ( let i=1; i<=100; i++ ) { 74 | const m = rnd(i*i), n = rnd(i*i); 75 | assert.equal( compare (fromInt(m)) (fromInt(n)) ("-1") ("0") ("1"), String(Number(m>n) - Number(m{ 79 | for ( let i=1; i<=100; i++ ) { 80 | const m = rnd(i*i), n = rnd(i*i); 81 | assert.equal( lt (fromInt(m)) (fromInt(n)) (false)(true), m < n ); 82 | assert.equal( le (fromInt(m)) (fromInt(n)) (false)(true), m <= n ); 83 | assert.equal( eq (fromInt(m)) (fromInt(n)) (false)(true), m == n ); 84 | assert.equal( ge (fromInt(m)) (fromInt(n)) (false)(true), m >= n ); 85 | assert.equal( gt (fromInt(m)) (fromInt(n)) (false)(true), m > n ); 86 | assert.equal( eq (fromInt(m)) (fromInt(m)) (false)(true), true ); 87 | } 88 | }); 89 | it("addition",()=>{ 90 | for ( let i=1; i<=100; i++ ) { 91 | const m = rnd(i*i), n = rnd(i*i); 92 | assert.equal( plus (m) (n), m + n ); 93 | } 94 | }); 95 | it("multiplication",()=>{ 96 | for ( let i=1; i<=100; i++ ) { 97 | const m = rnd(i*i), n = rnd(i*i); 98 | assert.equal( times (m) (n), m * n ); 99 | } 100 | }); 101 | it("subtraction",()=>{ 102 | for ( let i=1; i<=100; i++ ) { 103 | const m = rnd(i*i), n = rnd(i*i); 104 | assert.equal( minus (m) (n), Math.max( 0, m - n ) ); 105 | assert.equal( minus (n) (m), Math.max( 0, n - m ) ); 106 | } 107 | }); 108 | it("division",()=>{ 109 | for ( let i=1; i<=100; i++ ) { 110 | const m = rnd(i*i), n = rnd(i*i); 111 | assert.deepEqual( toPair( divMod (m) (n||1) ).map(toInt), [ m/(n||1)|0, m%(n||1) ] ); 112 | assert.deepEqual( toPair( divMod (n) (m||1) ).map(toInt), [ n/(m||1)|0, n%(m||1) ] ); 113 | } 114 | }); 115 | it("exponentiation",()=>{ 116 | for ( let i=1; i<=100; i++ ) { 117 | const m = rnd(i), n = rnd(i%10); 118 | assert.equal( pow (m) (n), m ** n ); 119 | } 120 | }); 121 | it("greatest common divisor",()=>{ 122 | for ( let i=1; i<=100; i++ ) { 123 | const m = rnd(i), n = rnd(i); 124 | assert.equal( gcd (m) (n), refGCD(m)(n) ); 125 | } 126 | }); 127 | it("least common multiple",()=>{ 128 | for ( let i=1; i<=100; i++ ) { 129 | const m = rnd(i), n = rnd(i); 130 | assert.equal( lcm (m) (n), m / (refGCD(m)(n)||1) * n ); 131 | } 132 | }); 133 | it("minimum",()=>{ 134 | for ( let i=1; i<=100; i++ ) { 135 | const m = rnd(i*i), n = rnd(i*i); 136 | assert.equal( min (m) (n), Math.min(m,n) ); 137 | } 138 | }); 139 | it("maximum",()=>{ 140 | for ( let i=1; i<=100; i++ ) { 141 | const m = rnd(i*i), n = rnd(i*i); 142 | assert.equal( max (m) (n), Math.max(m,n) ); 143 | } 144 | }); 145 | it("shifting bits",()=>{ 146 | for ( let i=1; i<=100; i++ ) { 147 | const n = rnd(i*i); 148 | assert.equal( shiftL (n), n >> 1 ); 149 | assert.equal( dbl (n), n << 1 ); 150 | assert.equal( shiftR1 (n), n << 1 | 1 ); 151 | } 152 | }); 153 | it("zero padding",()=>{ 154 | for ( let i=1; i<=100; i++ ) { 155 | const n = rnd(i*i); 156 | assert.equal( isPadded (n) (false)(true), false ); 157 | assert.equal( isPadded (pad(n)) (false)(true), true ); 158 | assert.equal( isPadded (pad(pad(n))) (false)(true), true ); 159 | assert.equal( isPadded (pad(pad(pad(n)))) (false)(true), true ); 160 | } 161 | }); 162 | it("bitwise and",()=>{ 163 | for ( let i=1; i<=100; i++ ) { 164 | const m = rnd(i*i), n = rnd(i*i); 165 | assert.equal( bitAnd (m) (n), m & n ); 166 | } 167 | }); 168 | it("bitwise or",()=>{ 169 | for ( let i=1; i<=100; i++ ) { 170 | const m = rnd(i*i), n = rnd(i*i); 171 | assert.equal( bitOr (m) (n), m | n ); 172 | } 173 | }); 174 | it("bitwise exclusive or",()=>{ 175 | for ( let i=1; i<=100; i++ ) { 176 | const m = rnd(i*i), n = rnd(i*i); 177 | assert.equal( bitXor (m) (n), m ^ n ); 178 | } 179 | }); 180 | it("testing bits",()=>{ 181 | for ( let i=1; i<=100; i++ ) { 182 | const j = rnd(i%32), n = rnd(i*i); 183 | assert.equal( testBit (j) (n) (false)(true), Boolean( n & 1<{ 187 | for ( let i=1; i<=100; i++ ) { 188 | const j = rnd(i%32); 189 | assert.equal( bit (j), 1<{ 193 | const refPopCount = n => n && 1 + refPopCount(n & n-1) ; 194 | for ( let i=1; i<=100; i++ ) { 195 | const n = rnd(i*i); 196 | assert.equal( popCount (n), refPopCount(n) ); // JS restricted to 32-bit 197 | } 198 | }); 199 | it("logical not",()=>{ 200 | assert.equal( not(False) (false)(true), true ); 201 | assert.equal( not(True) (false)(true), false ); 202 | }); 203 | it("logical and",()=>{ 204 | assert.equal( and(False)(False) (false)(true), false ); 205 | assert.equal( and(False)(True) (false)(true), false ); 206 | assert.equal( and(True) (False) (false)(true), false ); 207 | assert.equal( and(True) (True) (false)(true), true ); 208 | }); 209 | it("logical or",()=>{ 210 | assert.equal( or(False)(False) (false)(true), false ); 211 | assert.equal( or(False)(True) (false)(true), true ); 212 | assert.equal( or(True) (False) (false)(true), true ); 213 | assert.equal( or(True) (True) (false)(true), true ); 214 | }); 215 | it("logical exclusive or",()=>{ 216 | assert.equal( xor(False)(False) (false)(true), false ); 217 | assert.equal( xor(False)(True) (false)(true), true ); 218 | assert.equal( xor(True) (False) (false)(true), true ); 219 | assert.equal( xor(True) (True) (false)(true), false ); 220 | }); 221 | it("logical implies",()=>{ 222 | assert.strictEqual( implies(False)(False) (false)(true), true ); 223 | assert.strictEqual( implies(False)(True) (false)(true), true ); 224 | assert.strictEqual( implies(True) (False) (false)(true), false ); 225 | assert.strictEqual( implies(True) (True) (false)(true), true ); 226 | }); 227 | it("parity",()=>{ 228 | for ( let i=1; i<=100; i++ ) { 229 | const n = rnd(i*i*i); 230 | assert.equal( odd (fromInt(n)) (false)(true), Boolean(n&1) ); 231 | assert.equal( even (fromInt(n)) (false)(true), ! (n&1) ); 232 | } 233 | }); 234 | }); 235 | -------------------------------------------------------------------------------- /tests/basics-church/solution.txt: -------------------------------------------------------------------------------- 1 | # Church Number 2 | 3 | #import Combinators.lc 4 | 5 | B = \ f g x . f (g x) 6 | C = \ f x y . f y x 7 | I = \ x . x 8 | K = \ x _ . x 9 | KI = \ _ x . x 10 | M = \ x . x x 11 | S = \ f g x . f x (g x) 12 | T = \ x f . f x 13 | V = \ x y f . f x y 14 | W = \ f x . f x x 15 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 16 | Z = \ f . ( \ x . f \ y . x x y ) ( \ x . f \ y . x x y ) 17 | 18 | #import ChurchBoolean.lc 19 | 20 | True = \ t f . t 21 | False = \ t f . f 22 | 23 | not = \ b . b False True 24 | and = \ a b . a b a 25 | or = \ a . a a 26 | xor = \ a b . a (not b) b 27 | implies = \ a b . a b True 28 | 29 | # data Number = Zero | Succ Zero 30 | 31 | zero = KI 32 | 33 | isZero = \ n . n (K False) True 34 | 35 | # instance Enum 36 | 37 | succ = \ n . \ f x . f (n f x) 38 | pred = \ n . \ f x . n ( \ g h . h (g f) ) (K x) I 39 | pred = \ m . m ( \ k n m . k m (succ m) ) K zero zero 40 | 41 | # instance Num 42 | 43 | plus = \ m n . \ f x . n f (m f x) 44 | times = \ m n . \ f . n (m f) 45 | pow = \ m n . n m 46 | minus = \ m n . n pred m 47 | 48 | # instance Ord 49 | 50 | le = \ m n . isZero (minus m n) 51 | ge = \ m n . isZero (minus n m) 52 | eq = \ m n . and (le m n) (ge m n) 53 | lt = \ m n . ( \ le ge . and le (not ge) ) (le m n) (ge m n) 54 | gt = \ m n . ( \ le ge . and ge (not le) ) (le m n) (ge m n) 55 | -------------------------------------------------------------------------------- /tests/basics-church/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert} from "chai"; 3 | 4 | import * as LC from "../../src/lambda-calculus.js"; 5 | LC.configure({ purity: "LetRec", numEncoding: "Church" }); 6 | 7 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 8 | const solution = LC.compile(solutionText); 9 | const { fromInt, toInt } = LC; 10 | 11 | const {B,C,I,KI,M,S,T,V,W,Y,Z} = solution; 12 | const {True,False,not,and,or,xor,implies} = solution; 13 | const {lt,le,eq,ge,gt} = solution; 14 | const {zero,succ,pred,isZero} = solution; 15 | const {plus,times,pow,minus} = solution; 16 | 17 | const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; 18 | 19 | describe("Church tests",function(){ 20 | this.timeout(0); 21 | it("fixed tests",()=>{ 22 | LC.configure({ purity: "LetRec", numEncoding: "Church" }); 23 | const one = succ(zero); 24 | const two = succ(one); 25 | const three = succ(two); 26 | const four = succ(three); 27 | const five = succ(four); 28 | assert.equal( zero, 0 ); 29 | assert.equal( one, 1 ); 30 | assert.equal( two, 2 ); 31 | assert.equal( three, 3 ); 32 | assert.equal( four, 4 ); 33 | assert.equal( five, 5 ); 34 | const n = 1e3; 35 | assert.equal( I(fromInt(n)), n ); 36 | assert.equal( times(1e2)(1e1), 1e3 ); 37 | assert.equal( pow(10)(3), 1e3 ); 38 | assert.equal( pred(pow(10)(3)), 1e3-1 ); 39 | assert.equal( pow(0)(0), 1); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /tests/counter/solution.txt: -------------------------------------------------------------------------------- 1 | # Kacarott 2 | 3 | true = \ a b . a 4 | false = \ a b . b 5 | 6 | zero = false 7 | succ = \ n f x . f (n f x) 8 | 9 | y = \ f . (\ x . f (x x)) (\ x . f (x x)) 10 | 11 | counter = y (\ count n b . b (count (succ n)) (n) ) zero 12 | -------------------------------------------------------------------------------- /tests/counter/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "Let", numEncoding: "Church" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const {counter} = LC.compile(solutionText); 10 | 11 | const T = t => f => t ; 12 | const F = t => f => f ; 13 | 14 | describe("counter", () => { 15 | it("fixed tests", function() { 16 | LC.configure({ purity: "Let", numEncoding: "Church" }); 17 | assert.equal( counter(T)(T)(T)(F), 3 ); 18 | assert.equal( counter(T)(F), 1 ); 19 | assert.equal( counter(T)(T)(T)(T)(T)(T)(T)(F), 7 ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /tests/delta-generators/solution.txt: -------------------------------------------------------------------------------- 1 | # Kacarott 2 | 3 | true = \ a _ . a 4 | false = \ _ b . b 5 | id = \ x . x 6 | 7 | pair = \ a b c . c a b 8 | nil = \ p . p false () 9 | cons = \ v xs . pair true (pair v xs) 10 | 11 | sub = \ a b . b true (\ _ . false) a (sub (a a id) (b () id)) 12 | 13 | head = \ xs . xs false true 14 | tail = \ xs . xs false false 15 | null = \ xs. xs true false true 16 | 17 | ss = \ xs . sub (head (tail xs)) (head xs) 18 | 19 | d = \ xs . null xs nil (null (tail xs) nil (cons (ss xs) (d (tail xs)))) 20 | delta = \ n xs . n xs (\ m . delta m (d xs)) 21 | -------------------------------------------------------------------------------- /tests/delta-generators/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert} from "chai"; 3 | 4 | import * as LC from "../../src/lambda-calculus.js"; 5 | LC.configure({ purity: "LetRec", numEncoding: "Scott" }); 6 | 7 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 8 | const {delta} = LC.compile(solutionText); 9 | 10 | const {fin} = LC.compile(String.raw` 11 | nil = \ _ _ x . x 12 | cons = \ v l a . a (\ x _ . x) (\ b . b v l) 13 | fin = cons 1 (cons 2 (cons 4 (cons 7 nil)))`); 14 | 15 | const {inf} = LC.compile(String.raw` 16 | succ = \ n _ f . f n 17 | incr = \ n a . a (\ x _ . x) (\ b . b n (incr (succ n))) 18 | inf = incr 0`); 19 | 20 | const {toInt} = LC; 21 | 22 | const Fst = fst => snd => fst ; 23 | const Snd = fst => snd => snd ; 24 | const head = xs => xs (Snd) (Fst) ; 25 | const tail = xs => xs (Snd) (Snd) ; 26 | const isNil = xs => xs (Fst) (false) (true) ; 27 | 28 | function toArr(a, n) { // lists use double pair encoding, not Scott! 29 | const res = []; 30 | while ( n-->0 && ! isNil (a) ) 31 | res.push(toInt(head(a))), 32 | a = tail(a); 33 | return res; 34 | } 35 | 36 | describe("delta-generators", () => { 37 | it("fixed tests", function() { 38 | LC.configure({ purity: "LetRec", numEncoding: "Scott" }); 39 | assert.deepEqual( toArr( delta (2) (fin), 2 ), [1, 1] ); 40 | assert.deepEqual( toArr( delta (1) (inf), 10 ), [1,1,1,1,1,1,1,1,1,1] ); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /tests/hello-world/solution.txt: -------------------------------------------------------------------------------- 1 | # JohanWiltink 2 | 3 | true = \ t f . t 4 | false = \ t f . f 5 | pair = \ x y fn . fn x y 6 | nil = pair false (pair () ()) 7 | cons = \ x xs . pair true (pair x xs) 8 | 9 | # Code 10 | hello = cons 72 11 | ( cons 101 12 | ( cons 108 13 | ( cons 108 14 | ( cons 111 15 | ( cons 44 16 | ( cons 32 17 | ( cons 119 18 | ( cons 111 19 | ( cons 114 20 | ( cons 108 21 | ( cons 100 22 | ( cons 33 23 | nil 24 | )))))))))))) 25 | -------------------------------------------------------------------------------- /tests/hello-world/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "Let", numEncoding: "Church" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const {hello} = LC.compile(solutionText); 10 | 11 | const Fst = fst => snd => fst ; 12 | const Snd = fst => snd => snd ; 13 | 14 | const isNil = xs => xs (Fst) (false) (true) ; 15 | const head = xs => xs (Snd) (Fst) ; 16 | const tail = xs => xs (Snd) (Snd) ; 17 | 18 | // double pair encoding for list 19 | const toString = xs => isNil (xs) ? "" : String.fromCharCode(LC.toInt(head(xs))) + toString(tail(xs)) ; 20 | 21 | describe("hello-world", () => { 22 | it("fixed test", function() { 23 | LC.configure({ purity: "Let", numEncoding: "Church" }); 24 | assert.equal( toString(hello), "Hello, world!" ); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /tests/is-prime-scott/solution.txt: -------------------------------------------------------------------------------- 1 | # JohanWiltink 2 | 3 | B = \ f g x . f (g x) 4 | I = \ x . x 5 | K = \ x _ . x 6 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 7 | True = \ t _f . t 8 | False = \ _t f . f 9 | Succ = \ n _z s . s n 10 | Pred = \ n . n 0 I 11 | Plus = \ m n . m n (B Succ (Plus n)) 12 | Minus = \ m n . m 0 (B (n m) Minus) 13 | Times = \ m n . m 0 (B (Plus n) (Times n)) 14 | isZero = \ n . n True (K False) 15 | isNotZero = \ n . n False (K True) 16 | And = \ x y . x y x 17 | Or = \ x . x x 18 | GT = \ x y . isNotZero (Minus x y) 19 | Mod = \ n m . ( \ r . isZero (Minus m r) 0 r ) (( \ n m . ( \ d . isZero d n (Mod d m) ) (Minus n m) ) n m) 20 | 21 | # function isPrime(n) { 22 | # const trial = function trial(i) { 23 | # return i * i > n || n % i != 0 && trial(i+1) ; 24 | # } ; 25 | # return n > 1 && trial(2) ; 26 | # } 27 | 28 | isPrime = \ n . ( \ trial . And (GT n 1) (trial 2) ) 29 | ( Y \ trial i . Or (GT (Times i i) n) (And (isNotZero (Mod n i)) (trial (Succ i))) ) 30 | -------------------------------------------------------------------------------- /tests/is-prime-scott/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "LetRec", numEncoding: "Scott" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const {isPrime} = LC.compile(solutionText); 10 | 11 | describe("is-prime-scott",function(){ 12 | this.timeout(12e3); 13 | it("fixed tests",()=>{ 14 | LC.configure({ purity: "LetRec", numEncoding: "Scott" }); 15 | assert.equal( isPrime( 0) (true)(false), false ); 16 | assert.equal( isPrime( 1) (true)(false), false ); 17 | assert.equal( isPrime( 2) (true)(false), true ); 18 | assert.equal( isPrime( 3) (true)(false), true ); 19 | assert.equal( isPrime( 4) (true)(false), false ); 20 | assert.equal( isPrime( 5) (true)(false), true ); 21 | assert.equal( isPrime( 6) (true)(false), false ); 22 | assert.equal( isPrime( 7) (true)(false), true ); 23 | assert.equal( isPrime( 8) (true)(false), false ); 24 | assert.equal( isPrime( 9) (true)(false), false ); 25 | assert.equal( isPrime(10) (true)(false), false ); 26 | assert.equal( isPrime(11) (true)(false), true ); 27 | assert.equal( isPrime(12) (true)(false), false ); 28 | assert.equal( isPrime(13) (true)(false), true ); 29 | assert.equal( isPrime(14) (true)(false), false ); 30 | assert.equal( isPrime(15) (true)(false), false ); 31 | assert.equal( isPrime(16) (true)(false), false ); 32 | assert.equal( isPrime(17) (true)(false), true ); 33 | assert.equal( isPrime(18) (true)(false), false ); 34 | assert.equal( isPrime(19) (true)(false), true ); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/is-prime/solution.txt: -------------------------------------------------------------------------------- 1 | # JohanWiltink 2 | 3 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 4 | True = \ t _f . t 5 | False = \ _t f . f 6 | Succ = \ n s z . s (n s z) 7 | Pred = \ n s z . n ( \ f g . g (f s) ) (\_.z) \x.x 8 | Minus = \ m n . n Pred m 9 | Times = \ m n s . m (n s) 10 | isZero = \ n . n (\_.False) True 11 | isNotZero = \ n . n (\_.True) False 12 | And = \ x y . x y x 13 | Or = \ x . x x 14 | GT = \ x y . isNotZero (Minus x y) 15 | Mod = \ n m . ( \ r . isZero (Minus m r) 0 r ) (( \ n m . ( \ d . isZero d n (Mod d m) ) (Minus n m) ) n m) 16 | 17 | # function isPrime(n) { 18 | # const trial = function trial(i) { 19 | # return i * i > n || n % i != 0 && trial(i+1) ; 20 | # } ; 21 | # return n > 1 && trial(2) ; 22 | # } 23 | 24 | isPrime = \ n . ( \ trial . And (GT n 1) (trial 2) ) 25 | ( Y \ trial i . Or (GT (Times i i) n) (And (isNotZero (Mod n i)) (trial (Succ i))) ) 26 | -------------------------------------------------------------------------------- /tests/is-prime/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "LetRec", numEncoding: "Church" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const {isPrime} = LC.compile(solutionText); 10 | 11 | describe("is-prime",function(){ 12 | this.timeout(12e3); 13 | it("fixed tests",()=>{ 14 | LC.configure({ purity: "LetRec", numEncoding: "Church" }); 15 | assert.equal( isPrime(0) (true)(false), false ); 16 | assert.equal( isPrime(1) (true)(false), false ); 17 | assert.equal( isPrime(2) (true)(false), true ); 18 | assert.equal( isPrime(3) (true)(false), true ); 19 | assert.equal( isPrime(4) (true)(false), false ); 20 | assert.equal( isPrime(5) (true)(false), true ); 21 | assert.equal( isPrime(6) (true)(false), false ); 22 | assert.equal( isPrime(7) (true)(false), true ); 23 | assert.equal( isPrime(8) (true)(false), false ); 24 | assert.equal( isPrime(9) (true)(false), false ); 25 | assert.equal( isPrime(10) (true)(false), false ); 26 | assert.equal( isPrime(11) (true)(false), true ); 27 | assert.equal( isPrime(12) (true)(false), false ); 28 | assert.equal( isPrime(13) (true)(false), true ); 29 | assert.equal( isPrime(14) (true)(false), false ); 30 | assert.equal( isPrime(15) (true)(false), false ); 31 | assert.equal( isPrime(16) (true)(false), false ); 32 | assert.equal( isPrime(17) (true)(false), true ); 33 | assert.equal( isPrime(18) (true)(false), false ); 34 | assert.equal( isPrime(19) (true)(false), true ); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /tests/known-bugs/solution.txt: -------------------------------------------------------------------------------- 1 | foo = \ x j c . x c () 2 | -------------------------------------------------------------------------------- /tests/known-bugs/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "Let", numEncoding: "None" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const { foo } = LC.compile(solutionText); 10 | 11 | describe("No side effects", () => { 12 | it("The initial failed call used to cause the second call to behave weirdly", () => { 13 | try { 14 | foo("hi")("there")("world") 15 | } catch {} 16 | assert.strictEqual( foo(null).term.toString(), "\\ j c . x c ()" ); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/multiply/initialSolution.txt: -------------------------------------------------------------------------------- 1 | multiply = \ m n . n (m s) z 2 | -------------------------------------------------------------------------------- /tests/multiply/solution.txt: -------------------------------------------------------------------------------- 1 | multiply = \ m n s z . n (m s) z 2 | -------------------------------------------------------------------------------- /tests/multiply/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | 3 | import {assert, config as chaiConfig} from "chai"; 4 | chaiConfig.truncateThreshold = 0; 5 | 6 | import * as LC from "../../src/lambda-calculus.js"; 7 | LC.configure({ purity: "LetRec", numEncoding: "Church", verbosity: "Concise" }); 8 | 9 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 10 | const {multiply} = LC.compile(solutionText); 11 | 12 | describe("Multiply",()=>{ 13 | 14 | it("example tests",()=>{ 15 | LC.configure({ purity: "LetRec", numEncoding: "Church", verbosity: "Concise" }); 16 | assert.equal( multiply(7)(7), 49 ); 17 | assert.equal( multiply(11)(11), 121 ); 18 | }); 19 | 20 | it("random tests",()=>{ 21 | const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; 22 | for ( let i=1; i<=100; i++ ) { 23 | const m = rnd(i), n = rnd(i); 24 | assert.equal( multiply(m)(n), m*n ); 25 | } 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/negabinary-scott/solution.lc: -------------------------------------------------------------------------------- 1 | #debug 2 | 3 | #import combinators.lc 4 | B = \ f g x . f (g x) 5 | C = \ f x y . f y x 6 | K = \ x _ . x 7 | T = \ x f . f x 8 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 9 | 10 | #import church-boolean.lc 11 | True = \ true _false . true 12 | False = \ _true false . false 13 | 14 | #import church-ordering.lc 15 | LT = \ lt _eq _gt . lt 16 | EQ = \ _lt eq _gt . eq 17 | GT = \ _lt _eq gt . gt 18 | 19 | #import scott-pair.lc 20 | Pair = \ x y f . f x y 21 | fst = \ xy . xy \ x _y . x 22 | snd = \ xy . xy \ _x y . y 23 | bimap = \ fn xy . xy \ x y . Pair (fn x) (fn y) 24 | Y2 = B Y (C (B bimap T)) 25 | 26 | #import scott-quad.lc 27 | Quad = \ w x y z f . f w x y z 28 | fst4 = \ wxyz . wxyz \ w _x _y _z . w 29 | snd4 = \ wxyz . wxyz \ _w x _y _z . x 30 | thd4 = \ wxyz . wxyz \ _w _x y _z . y 31 | fth4 = \ wxyz . wxyz \ _w _x _y z . z 32 | quadmap = \ fn wxyz . wxyz \ w x y z . Quad (fn w) (fn x) (fn y) (fn z) 33 | Y4 = B Y (C (B quadmap T)) 34 | 35 | #export scott-negabinary-number.lc 36 | 37 | # NegaBinary = Zero | Bit0 NegaBinary | Bit1 NegaBinary 38 | 39 | Bit0 = \ n . \ _end even _odd . even n 40 | Bit1 = \ n . \ _end _even odd . odd n 41 | 42 | nega-dbl = \ n . n 0 (K (Bit0 n)) (K (Bit0 n)) 43 | 44 | Enum = Y2 (Pair (T \ succ pred . \ m . m 1 Bit1 (B nega-dbl pred)) # succ 45 | (T \ succ pred . \ m . m -1 (B Bit1 succ) nega-dbl) # pred 46 | ) 47 | succ = fst Enum 48 | pred = snd Enum 49 | 50 | add = \ m n . m n 51 | ( \ zm . n m ( \ zn . nega-dbl (add zm zn) ) ( \ zn . Bit1 (add zm zn) ) ) 52 | ( \ zm . n m ( \ zn . Bit1 (add zm zn) ) ( \ zn . nega-dbl (pred (add zm zn)) ) ) 53 | mul = \ m n . m 0 54 | ( \ zm . n 0 ( \ zn . Bit0 (Bit0 (mul zm zn)) ) ( \ _z . Bit0 (mul zm n) ) ) 55 | ( \ zm . n 0 ( \ zn . Bit0 (mul m zn) ) ( \ zn . Bit1 (add (nega-dbl (mul zm zn)) (add zm zn)) ) ) 56 | 57 | negate = \ n . add n (nega-dbl n) 58 | sub = \ m n . add m (negate n) 59 | dbl = \ m . m 0 ( \ n . Bit0 (dbl n) ) ( \ n . nega-dbl (pred (dbl n)) ) 60 | zero = \ n . n True (K False) (K False) 61 | 62 | Ord = Y4 (Quad (T \ lt0 le0 ge0 gt0 . \ n . n False gt0 gt0) # lt0 63 | (T \ lt0 le0 ge0 gt0 . \ n . n True ge0 gt0) # le0 64 | (T \ lt0 le0 ge0 gt0 . \ n . n True le0 le0) # ge0 65 | (T \ lt0 le0 ge0 gt0 . \ n . n False lt0 le0) # gt0 66 | ) 67 | lt0 = fst4 Ord 68 | le0 = snd4 Ord 69 | ge0 = thd4 Ord 70 | gt0 = fth4 Ord 71 | 72 | compare = \ m n . T (sub m n) \ d . zero d EQ (lt0 d LT GT) # church boolean 73 | -------------------------------------------------------------------------------- /tests/negabinary-scott/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert} from "chai"; 3 | 4 | import * as LC from "../../src/lambda-calculus.js"; 5 | 6 | const Zero = end => even => odd => end ; 7 | const Bit0 = n => end => even => odd => even(n) ; 8 | const Bit1 = n => end => even => odd => odd (n) ; 9 | const fromInt = n => n ? n&1 ? Bit1(fromInt(-(n>>1))) : Bit0(fromInt(-(n>>1))) : Zero ; 10 | const padded = m => m (false) ( n => n (true) ( () => padded(n) ) (padded) ) (padded) ; 11 | const unsafeToInt = m => m (0) ( n => - 2 * unsafeToInt(n) ) ( n => 1 - 2 * unsafeToInt(n) ) ; 12 | const toInt = n => { 13 | if ( padded(n) ) 14 | throw new TypeError(`toInt: padded number ${ unsafeToInt(n) }`); 15 | else 16 | return unsafeToInt(n); 17 | } ; 18 | LC.configure({ purity: "LetRec", numEncoding: { fromInt, toInt } }); 19 | 20 | const solutionText = readFileSync(new URL("./solution.lc", import.meta.url), {encoding: "utf8"}); 21 | const solution = LC.compile(solutionText); 22 | const { succ,pred, add,mul,negate,sub,dbl, zero, lt0,le0,ge0,gt0,compare } = solution; 23 | 24 | const toBoolean = p => p (true) (false) ; 25 | const toOrdering = cmp => cmp ("LT") ("EQ") ("GT") ; 26 | 27 | describe("NegaBinaryScott", () => { 28 | it("numbers", () => { 29 | LC.configure({ purity: "LetRec", numEncoding: { fromInt, toInt } }); 30 | for ( let n=-10; n<=10; n++ ) 31 | assert.strictEqual( toInt(fromInt(n)), n, `toInt (fromInt ${ n })` ); 32 | }); 33 | it("succ", () => { 34 | for ( let n=-10; n<=10; n++ ) 35 | assert.strictEqual( toInt(succ(n)), n+1, `succ ${ n }` ); 36 | }); 37 | it("pred", () => { 38 | for ( let n=-10; n<=10; n++ ) 39 | assert.strictEqual( toInt(pred(n)), n-1, `pred ${ n }` ); 40 | }); 41 | it("add", () => { 42 | for ( let m=-10; m<=10; m++ ) 43 | for ( let n=-10; n<=10; n++ ) 44 | assert.strictEqual( toInt(add(m)(n)), m+n, `add ${ m } ${ n }` ); 45 | }); 46 | it("mul", () => { 47 | for ( let m=-10; m<=10; m++ ) 48 | for ( let n=-10; n<=10; n++ ) 49 | assert.strictEqual( toInt(mul(m)(n)), m*n, `mul ${ m } ${ n }` ); 50 | }); 51 | it("negate", () => { 52 | for ( let n=-10; n<=10; n++ ) 53 | assert.strictEqual( toInt(negate(n)), -n, `negate ${ n }` ); 54 | }); 55 | it("negate . negate", () => { 56 | for ( let n=-10; n<=10; n++ ) 57 | assert.strictEqual( toInt(negate(negate(n))), n, `negate (negate ${ n })` ); 58 | }); 59 | it("sub", () => { 60 | for ( let m=-10; m<=10; m++ ) 61 | for ( let n=-10; n<=10; n++ ) 62 | assert.strictEqual( toInt(sub(m)(n)), m-n, `sub ${ m } ${ n }` ); 63 | }); 64 | it("dbl", () => { 65 | for ( let n=-10; n<=10; n++ ) 66 | assert.strictEqual( toInt(dbl(n)), 2*n, `dbl ${ n }` ); 67 | }); 68 | it("eq, uneq", () => { 69 | for ( let n=-10; n<=10; n++ ) 70 | assert.strictEqual(toBoolean(zero(n)),n===0,`zero ${ n }`), 71 | assert.strictEqual(toBoolean(lt0(n)),n<0,`lt0 ${ n }`), 72 | assert.strictEqual(toBoolean(le0(n)),n<=0,`le0 ${ n }`), 73 | assert.strictEqual(toBoolean(ge0(n)),n>=0,`ge0 ${ n }`), 74 | assert.strictEqual(toBoolean(gt0(n)),n>0,`gt0 ${ n }`); 75 | }); 76 | it("compare", () => { 77 | for ( let m=-10; m<=10; m++ ) 78 | for ( let n=-10; n<=10; n++ ) 79 | assert.strictEqual( toOrdering(compare(m)(n)), m > n ? "GT" : m < n ? "LT" : "EQ" , `compare ${ m } ${ n }` ); 80 | }); 81 | }); -------------------------------------------------------------------------------- /tests/prime-sieve/solution-scott.txt: -------------------------------------------------------------------------------- 1 | # JohanWiltink 2 | 3 | # primes 4 | # according to The Real Sieve of Erathosthenes 5 | 6 | # B :: (b -> c) -> (a -> b) -> (a -> c) 7 | B = \ f g x . f (g x) 8 | 9 | # data Int :: Zero | Succ Int 10 | 11 | # succ :: Int -> Int 12 | succ = \ n . \ zero succ . succ n 13 | 14 | # plus :: Int -> Int -> Int 15 | plus = \ m n . n m (B succ (plus m)) 16 | 17 | # mult :: Int -> Int -> Int 18 | mult = \ m n . n n (B (plus m) (mult m)) 19 | 20 | # minus :: Int -> Int -> Int 21 | minus = \ m n . m m (B (n m) minus) 22 | 23 | # data Bool :: False | True 24 | 25 | # false :: Bool 26 | false = \ false true . false 27 | 28 | # true :: Bool 29 | true = \ false true . true 30 | 31 | # isZero :: Int -> Bool 32 | isZero = \ n . n true ( \ n-1 . false ) 33 | 34 | # EQ :: Int -> Int -> Bool 35 | EQ = \ m n . m (isZero n) (B (n false) EQ) 36 | 37 | # data Pair a b :: Pair a b 38 | 39 | # pair :: a -> b -> Pair a b 40 | pair = \ x y . \ pair . pair x y 41 | 42 | # fst :: Pair a b -> a 43 | fst = \ xy . xy ( \ x y . x ) 44 | 45 | # snd :: Pair a b -> b 46 | snd = \ xy . xy ( \ x y . y ) 47 | 48 | # data Stream a :: Cons a (Stream a) 49 | 50 | # cons :: a -> Stream a -> Stream a 51 | cons = \ x xs . \ cons . cons x xs 52 | 53 | # head :: Stream a -> a 54 | head = \ xs . xs ( \ x xs . x ) 55 | 56 | # tail :: Stream a -> Stream a 57 | tail = \ xs . xs ( \ x xs . xs ) 58 | 59 | # map :: (a -> b) -> Stream a -> Stream b 60 | map = \ fn xs . xs ( \ x xs . cons (fn x) (map fn xs) ) 61 | 62 | # iterate :: (a -> a) -> a -> Stream a 63 | iterate = \ fn x . cons x (iterate fn (fn x)) 64 | 65 | # LE :: Int -> Int -> Bool 66 | # LE = \ m n . isZero (minus m n) 67 | # LE = \ m n . m true (B (n false) LE) # probably equal performance 68 | 69 | # LE :: Stream Int -> Stream Int -> Bool 70 | LE = \ m n . isZero (minus (head m) (head n)) # no need to order on subsequent elements 71 | 72 | # data Set a = Nil | Branch a (Set a) (Set a) 73 | 74 | # empty :: Set a 75 | empty = \ nil branch . nil 76 | 77 | # branch :: a -> Set a -> Set a -> Set a 78 | branch = \ x left right . \ nil branch . branch x left right 79 | 80 | # insert :: (Ord a) => a -> Set a -> Set a 81 | insert = \ x set . 82 | set 83 | (branch x empty empty) 84 | ( \ y left right . 85 | LE x y 86 | (branch y left (insert x right)) 87 | (branch y (insert x left) right) 88 | ) 89 | 90 | # findMin :: (Partial) => Set a -> a 91 | findMin = \ set . set () 92 | \ x left right . 93 | left x ( \ _ _ _ . findMin left ) 94 | 95 | # minView :: (Partial) => Set a -> (a,Set a) 96 | minView = \ set . set () 97 | \ x left right . 98 | left 99 | (pair x right) 100 | ( \ _ _ _ . ( \ unpair . unpair \ y left' . pair y (branch x left' right) ) (minView left) ) 101 | 102 | # insertPrime :: Stream Int -> Set (Stream Int) -> Set (Stream Int) 103 | insertPrime = \ candidates . candidates 104 | \ prime _ . 105 | insert (map (mult prime) candidates) 106 | 107 | # adjust :: Int -> Set (Stream Int) -> Set (Stream Int) 108 | adjust = \ x table . 109 | ( \ unpair . unpair 110 | \ uncons table' . uncons 111 | \ n ns . 112 | EQ n x 113 | table 114 | (adjust x (insert ns table')) 115 | ) 116 | (minView table) 117 | 118 | # sieve :: Set (Stream Int) -> Stream Int -> Stream Int 119 | sieve = \ table xxs . xxs 120 | \ x xs . 121 | ( \ uncons . uncons 122 | \ n _ . 123 | EQ n x 124 | (cons x (sieve (insertPrime xxs table) xs)) 125 | (sieve (adjust x table) xs) 126 | ) 127 | (findMin table) 128 | 129 | # firstSieve :: Stream Int -> Stream Int 130 | firstSieve = \ xxs . xxs 131 | \ x xs . 132 | cons x (sieve (insertPrime xxs empty) xs) 133 | 134 | # primes :: Stream Int 135 | primes = cons 2 (firstSieve (iterate (plus 2) 3)) 136 | -------------------------------------------------------------------------------- /tests/prime-sieve/solution.txt: -------------------------------------------------------------------------------- 1 | # JohanWiltink 2 | 3 | # primes 4 | # according to The Real Sieve of Erathosthenes 5 | 6 | #import Combinators.lc 7 | 8 | B = \ f g x . f (g x) 9 | C = \ f x y . f y x 10 | I = \ x . x 11 | K = \ x _ . x 12 | KI = \ _ x . x 13 | M = \ x . x x 14 | S = \ f g x . f x (g x) 15 | T = \ x f . f x 16 | V = \ x y f . f x y 17 | W = \ f x . f x x 18 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 19 | Z = \ f . ( \ x . f \ y . x x y ) ( \ x . f \ y . x x y ) 20 | 21 | #import Ordering.lc 22 | 23 | LT = \ lt _eq _gt . lt 24 | EQ = \ _lt eq _gt . eq 25 | GT = \ _lt _eq gt . gt 26 | 27 | # import Booleans.lc 28 | 29 | False = \ false _true . false 30 | True = \ _false true . true 31 | 32 | # data Number = End | Even Number | Odd Number 33 | 34 | # zero :: Int 35 | zero = \ end _even _odd . end 36 | 37 | # shiftR0,shiftR1 :: Int -> Int 38 | shiftR0 = \ n . \ _end even _odd . even n # mind that a shiftR in LE is a multiplication 39 | shiftR1 = \ n . \ _end _even odd . odd n # mind that a shiftR in LE is a multiplication 40 | 41 | # isZero :: Int -> Bool 42 | isZero = \ n . n True (K False) (K False) 43 | 44 | # unpad :: Int -> Int 45 | unpad = \ n . n zero ( \ z . ( \ unpadZ . isZero unpadZ (shiftR0 unpadZ) zero ) (unpad z) ) (B shiftR1 unpad) 46 | 47 | # succ,pred :: Int -> Int 48 | succ = \ n . n (shiftR1 zero) shiftR1 (B shiftR0 succ) 49 | go = \ prefix n . n zero 50 | (go (B prefix shiftR1)) 51 | ( \ z . z (prefix z) (K (prefix (shiftR0 z))) (K (prefix (shiftR0 z))) ) 52 | pred = go I 53 | 54 | # compare :: Int -> Int -> Ordering 55 | compare = \ m n . m (n EQ (K LT) (K LT)) 56 | ( \ zm . n GT (compare zm) ( \ zn . compare zm zn LT LT GT ) ) 57 | ( \ zm . n GT ( \ zn . compare zm zn LT GT GT ) (compare zm) ) 58 | # eq,gt :: Int -> Int -> Bool 59 | eq = \ m n . compare m n False True False 60 | gt = \ m n . compare m n False False True 61 | 62 | # plus,mult,minus :: Int -> Int -> Int 63 | plus = \ m n . m n 64 | ( \ zm . n (shiftR0 zm) (B shiftR0 (plus zm)) (B shiftR1 (plus zm)) ) 65 | ( \ zm . n (shiftR1 zm) (B shiftR1 (plus zm)) (B shiftR0 (B succ (plus zm))) ) 66 | 67 | mult = \ m n . m m 68 | ( \ zm . n n 69 | ( \ zn . shiftR0 (shiftR0 (mult zm zn)) ) 70 | ( \ zn . shiftR0 (mult zm (shiftR1 zn)) ) 71 | ) 72 | ( \ zm . n n 73 | ( \ zn . shiftR0 (mult (shiftR1 zm) zn) ) 74 | ( \ zn . plus (shiftR1 zn) (shiftR0 (mult zm (shiftR1 zn))) ) 75 | ) 76 | unsafeMinus = \ m n . m zero 77 | ( \ zm . n (shiftR0 zm) (B shiftR0 (unsafeMinus zm)) (B shiftR1 (B pred (unsafeMinus zm))) ) 78 | ( \ zm . n (shiftR1 zm) (B shiftR1 (unsafeMinus zm)) (B shiftR0 (unsafeMinus zm)) ) 79 | minus = \ m n . gt m n zero (unpad (unsafeMinus m n)) # needs explicit unpad or will litter padding 80 | 81 | # data Pair a b :: Pair a b 82 | 83 | # pair :: a -> b -> Pair a b 84 | pair = \ x y . \ pair . pair x y 85 | 86 | # fst :: Pair a b -> a 87 | fst = \ xy . xy ( \ x _y . x ) 88 | 89 | # snd :: Pair a b -> b 90 | snd = \ xy . xy ( \ _x y . y ) 91 | 92 | # data Stream a :: Cons a (Stream a) 93 | 94 | # cons :: a -> Stream a -> Stream a 95 | cons = \ x xs . \ cons . cons x xs 96 | 97 | # head :: Stream a -> a 98 | head = \ xs . xs ( \ x _xs . x ) 99 | 100 | # tail :: Stream a -> Stream a 101 | tail = \ xs . xs ( \ _x xs . xs ) 102 | 103 | # map :: (a -> b) -> Stream a -> Stream b 104 | map = \ fn xs . xs ( \ x xs . cons (fn x) (map fn xs) ) 105 | 106 | # iterate :: (a -> a) -> a -> Stream a 107 | iterate = \ fn x . cons x (iterate fn (fn x)) 108 | 109 | # le :: Stream a -> Stream a -> Bool 110 | le = \ m n . compare (head m) (head n) True True False 111 | 112 | # data Set a = Nil | Branch a (Set a) (Set a) 113 | 114 | # empty :: Set a 115 | empty = \ nil _branch . nil 116 | 117 | # branch :: a -> Set a -> Set a -> Set a 118 | branch = \ x left right . \ _nil branch . branch x left right 119 | 120 | # insert :: (Ord a) => a -> Set a -> Set a 121 | insert = \ x set . 122 | set 123 | (branch x empty empty) 124 | ( \ y left right . 125 | le x y 126 | (branch y left (insert x right)) 127 | (branch y (insert x left) right) 128 | ) 129 | 130 | # findMin :: (Partial) => Set a -> a 131 | findMin = \ set . set () 132 | \ x left _right . 133 | left x ( \ _ _ _ . findMin left ) 134 | 135 | # minView :: (Partial) => Set a -> (a,Set a) 136 | minView = \ set . set () 137 | \ x left right . 138 | left 139 | (pair x right) 140 | ( \ _ _ _ . ( \ unpair . unpair \ y left' . pair y (branch x left' right) ) (minView left) ) 141 | 142 | # insertPrime :: Stream Int -> Set (Stream Int) -> Set (Stream Int) 143 | insertPrime = \ candidates . candidates 144 | \ prime _ . 145 | insert (map (mult prime) candidates) 146 | 147 | # adjust :: Int -> Set (Stream Int) -> Set (Stream Int) 148 | adjust = \ x table . 149 | ( \ unpair . unpair 150 | \ uncons table' . uncons 151 | \ n ns . 152 | eq n x 153 | table 154 | (adjust x (insert ns table')) 155 | ) 156 | (minView table) 157 | 158 | # sieve :: Set (Stream Int) -> Stream Int -> Stream Int 159 | sieve = \ table xxs . xxs 160 | \ x xs . 161 | ( \ uncons . uncons 162 | \ n _ . 163 | eq n x 164 | (cons x (sieve (insertPrime xxs table) xs)) 165 | (sieve (adjust x table) xs) 166 | ) 167 | (findMin table) 168 | 169 | # firstSieve :: Stream Int -> Stream Int 170 | firstSieve = \ xxs . xxs 171 | \ x xs . 172 | cons x (sieve (insertPrime xxs empty) xs) 173 | 174 | # primes :: Stream Int 175 | primes = cons 2 (firstSieve (iterate (plus 2) 3)) 176 | -------------------------------------------------------------------------------- /tests/prime-sieve/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const {primes} = LC.compile(solutionText); 10 | const head = xs => xs ( x => xs => x ) ; 11 | const tail = xs => xs ( x => xs => xs ) ; 12 | const take = n => xs => n ? [ head(xs), ...take(n-1)(tail(xs)) ] : [] ; 13 | 14 | describe("prime-sieve", () => { 15 | it("fixed tests: primes", function() { 16 | LC.configure({ purity: "LetRec", numEncoding: "BinaryScott" }); 17 | this.timeout(12e3); 18 | assert.equal( head(primes), 2 ); 19 | assert.equal( head(tail(primes)), 3 ); 20 | assert.equal( head(tail(tail(primes))), 5 ); 21 | assert.equal( head(tail(tail(tail(primes)))), 7 ); 22 | assert.deepEqual( take(100)(primes).map(LC.toInt), [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71 23 | ,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149 24 | ,151,157,163,167,173,179,181,191,193,197,199,211,223,227 25 | ,229,233,239,241,251,257,263,269,271,277,281,283,293,307 26 | ,311,313,317,331,337,347,349,353,359,367,373,379,383,389 27 | ,397,401,409,419,421,431,433,439,443,449,457,461,463,467 28 | ,479,487,491,499,503,509,521,523,541 29 | ] ); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /tests/scott-lists/solution.txt: -------------------------------------------------------------------------------- 1 | # scott-list.lc 2 | 3 | #import combinators.lc 4 | B = \ f g x . f (g x) 5 | BB = \ f g x y . f (g x y) 6 | CB = \ f g x . g (f x) 7 | C = \ f x y . f y x 8 | I = \ x . x 9 | K = \ x _ . x 10 | KI = \ _ x . x 11 | KK = \ x _ _ . x 12 | M = \ x . x x 13 | S = \ f g x . f x (g x) 14 | T = \ x fn . fn x 15 | V = \ x y fn . fn x y 16 | W = \ f x . f x x 17 | Y = \ f . ( \ x . f (x x) ) ( \ x . f (x x) ) 18 | #import scott-booleans.ls 19 | False = K 20 | True = KI 21 | not = C 22 | and = M 23 | or = W C 24 | #import scott-ordering.lc 25 | LT = KK 26 | EQ = K K 27 | GT = K KI 28 | #import scott-numbers.lc 29 | zero = K 30 | succ = \ n . \ _zero succ . succ n 31 | pred = \ n . n zero I 32 | add = \ m n . m n (B succ (add n)) 33 | sub = \ m n . m m (B (n m) sub) 34 | mul = \ m n . m m (B (add n) (mul n)) 35 | is-zero = \ n . n True (K False) 36 | one = succ zero 37 | compare = \ m n . m (n EQ LT) (B (n GT) compare) 38 | lt = \ m n . compare m n True False False 39 | le = \ m n . compare m n True True False 40 | eq = \ m n . compare m n False True False 41 | ge = \ m n . compare m n False True True 42 | gt = \ m n . compare m n False False True 43 | #import scott-pair.lc 44 | Pair = V 45 | fst = T K 46 | snd = T KI 47 | first = \ fn xy . xy \ x . Pair (fn x) 48 | second = \ fn xy . xy \ x y . Pair x (fn y) 49 | both = \ fn xy . xy \ x y . Pair (fn x) (fn y) 50 | bimap = \ f g xy . xy \ x y . Pair (f x) (g y) 51 | curry = \ fn x y . fn (Pair x y) 52 | uncurry = T 53 | #import scott-option.lc 54 | None = K # = zero 55 | Some = \ x . \ _none some . some x # = succ 56 | option = V 57 | is-none = \ x . x True (K False) # = is-zero 58 | is-some = \ x . x False (K True) 59 | from-option = \ z x . x z I 60 | from-some = \ x . x () I 61 | # additional definitions depend on nil, cons, singleton 62 | 63 | # data List a = Nil | Cons a (List a) 64 | 65 | # nil :: List a 66 | nil = K 67 | 68 | # cons :: a -> List a -> List a 69 | cons = \ x xs . \ _nil cons . cons x xs 70 | 71 | # singleton :: a -> List a 72 | singleton = \ x . cons x nil 73 | 74 | # these scott-option definitions depend on nil, cons, singleton 75 | list-to-option = \ xs . xs None \ x _xs . Some x 76 | option-to-list = \ x . x nil singleton 77 | map-option = \ fn xs . xs nil \ x xs . fn x (map-option fn xs) (C cons (map-option fn xs)) 78 | cat-options = map-option I 79 | 80 | # continuing scott-list.lc 81 | 82 | # foldr :: (a -> z -> z) -> z -> List a -> z 83 | foldr = \ fn z xs . xs z \ x xs . fn x (foldr fn z xs) 84 | 85 | # foldl :: (z -> a -> z) -> z -> List a -> z 86 | foldl = \ fn z xs . xs z (B (foldl fn) (fn z)) 87 | 88 | # scanr :: (a -> z -> z) -> z -> List a -> List z 89 | scanr = \ fn z xs . xs (singleton z) \ x xs . ( \ zs . zs () \ z _zs . cons (fn x z) zs ) (scanr fn z xs) 90 | 91 | # scanl :: (z -> a -> z) -> z -> List a -> List z 92 | scanl = \ fn z xs . cons z (xs nil (B (scanl fn) (fn z))) 93 | 94 | # take :: Number -> List a -> List a 95 | take = \ n xs . is-zero n (xs nil \ x xs . cons x (take (pred n) xs)) nil 96 | 97 | # drop :: Number -> List a -> List a 98 | drop = \ n xs . is-zero n (xs nil (K (drop (pred n)))) xs 99 | 100 | # append :: List a -> List a -> List a 101 | append = C (foldr cons) 102 | 103 | # concat :: List (List a) -> List a 104 | concat = foldr append nil 105 | 106 | # snoc :: List a -> a -> List a 107 | snoc = C (B (foldr cons) singleton) 108 | 109 | # uncons :: List a -> Option (Pair a (List a)) 110 | uncons = \ xs . xs None (BB Some Pair) 111 | 112 | # iterate :: (a -> a) -> a -> List a 113 | iterate = \ fn x . cons x (iterate fn (fn x)) 114 | 115 | # repeat :: a -> List a 116 | repeat = \ x . cons x (repeat x) # repeat = Y (S cons) 117 | 118 | # cycle :: List a -> List a 119 | cycle = \ xs . xs () \ _x _xs . concat (repeat xs) 120 | 121 | # replicate :: Number -> a -> List a 122 | replicate = \ n . B (take n) repeat 123 | 124 | # unfold :: (a -> Option (Pair z a)) -> a -> List z 125 | unfold = \ fn x . fn x nil (T \ z x . cons z (unfold fn x)) 126 | 127 | # head :: List a -> a 128 | head = \ xs . xs () K 129 | 130 | # tail :: List a -> List a 131 | tail = \ xs . xs () KI 132 | 133 | # null :: List a -> Boolean 134 | null = \ xs . xs True (KK False) 135 | 136 | # length :: List a -> Number 137 | length = foldr (K succ) zero 138 | 139 | # sum,product :: List Number -> Number 140 | sum = foldr add zero 141 | product = foldr mul one 142 | 143 | # map :: (a -> b) -> List a -> List b 144 | map = \ fn . foldr (B cons fn) nil 145 | 146 | # concat-map :: (a -> List b) -> List a -> List b 147 | concat-map = BB concat map 148 | 149 | # filter :: (a -> Boolean) -> List a -> List a 150 | filter = \ p . foldr ( \ x z . p x z (cons x z) ) nil 151 | 152 | # take-while :: (a -> Boolean) -> List a -> List a 153 | take-while = \ p xs . xs nil \ x xs . p x nil (cons x (take-while p xs)) 154 | 155 | # drop-while :: (a -> Boolean) -> List a -> List a 156 | drop-while = \ p xxs . xxs nil \ x xs . p x xxs (drop-while p xs) 157 | 158 | # drop-while-end :: (a -> Boolean) -> List a -> List a 159 | drop-while-end = \ p . foldr ( \ x z . and (null z) (p x) (cons x z) nil ) nil 160 | 161 | # split-at :: Number -> List a -> Pair (List a) (List a) 162 | split-at = \ i xs . is-zero i (xs (Pair nil nil) \ x xs . first (cons x) (split-at (pred i) xs)) (Pair nil xs) 163 | 164 | # get :: Number -> List a -> a 165 | get = \ i xs . is-zero i (xs () (K (get (pred i)))) (head xs) 166 | 167 | # set :: Number -> a -> List a -> List a 168 | set = \ i x xs . uncurry append (second (B (cons x) tail) (split-at i xs)) 169 | set = \ i x xs . is-zero i (xs nil \ y . cons y (set (pred i) x)) (xs nil (K (cons x))) 170 | 171 | # any :: (a -> Boolean) -> List a -> Boolean 172 | any = \ p . foldr (B or p) False 173 | 174 | # all :: (a -> Boolean) -> List a -> Boolean 175 | all = \ p . foldr (B and p) True 176 | 177 | # find :: (a -> Boolean) -> List a -> Option a 178 | find = BB list-to-option filter 179 | 180 | # find-indices :: (a -> Boolean) -> List a -> List Number 181 | find-indices = \ p . foldr ( \ x k i . p x I (cons i) (k (succ i)) ) (K nil) zero 182 | 183 | # find-index :: (a -> Boolean) -> List a -> Option Number 184 | find-index = BB list-to-option find-indices 185 | 186 | # partition :: (a -> Boolean) -> List a -> Pair (List a) (List a) 187 | partition = \ p . foldr ( \ x . p x second first (cons x) ) (Pair nil nil) 188 | 189 | # span :: (a -> Boolean) -> List a -> Pair (List a) (List a) 190 | span = \ p xs . xs (Pair nil nil) \ y ys . p y (Pair nil xs) (first (cons y) (span p ys)) 191 | 192 | # minimum-by :: (a -> a -> Boolean) -> List a -> a 193 | minimum-by = \ le xs . xs () (foldl \ z x . le z x x z) 194 | 195 | # maximum-by :: (a -> a -> Boolean) -> List a -> a 196 | maximum-by = \ le xs . xs () (foldl \ z x . le z x z x) 197 | 198 | # insert-by :: (a -> a -> Boolean) -> a -> List a -> List a 199 | insert-by = \ le x xs . uncurry append (second (cons x) (span (C le x) xs)) 200 | 201 | # sort-by :: (a -> a -> Boolean) -> List a -> List a 202 | sort-by = \ le . foldr (insert-by le) nil 203 | # has all sorts of bad implementation details, but it's simple 204 | 205 | # compare-by :: (a -> a -> Ordering) -> List a -> List a -> Ordering 206 | compare-by = \ cmp xs ys . xs (ys EQ LT) 207 | \ x xs . ys GT 208 | \ y ys . cmp x y LT 209 | (compare-by cmp xs ys) 210 | GT 211 | 212 | # reverse :: List a -> List a 213 | reverse = foldl (C cons) nil 214 | 215 | # zip-with :: (a -> b -> z) -> List a -> List b -> List z 216 | zip-with = \ fn xs ys . xs nil \ x xs . ys nil \ y ys . cons (fn x y) (zip-with fn xs ys) 217 | 218 | # zip :: List a -> List b -> List (Pair a b) 219 | zip = zip-with Pair 220 | 221 | # unzip :: List (Pair a b) -> Pair (List a) (List b) 222 | unzip = foldr ( \ xy xys . xy \ x y . bimap (cons x) (cons y) xys ) (Pair nil nil) 223 | unzip = foldr (CB \ x y . bimap (cons x) (cons y)) (Pair nil nil) 224 | 225 | # group-by :: (a -> a -> Bool) -> List a -> List (List a) 226 | group-by = \ eq xs . xs nil \ x xs . span (eq x) xs \ left right . cons (cons x left) (group-by eq right) 227 | 228 | # lookup-by :: (a -> Boolean) -> List (Pair a b) -> Option b 229 | lookup-by = \ p xys . xys None \ xy xys . xy \ x y . p x (lookup-by p xys) (Some y) 230 | 231 | # nub-by :: (a -> a -> Boolean) -> List a -> List a 232 | go = \ z eq xs . xs z \ x xs . go (is-none (find (eq x) z) z (cons x z)) eq xs 233 | nub-by = go nil 234 | 235 | # delete-by :: (a -> a -> Boolean) -> a -> List a -> List a 236 | delete-by = \ eq x xs . xs nil \ y ys . eq x y (cons y (delete-by eq x ys)) ys 237 | 238 | # delete-firsts-by :: (a -> a -> Boolean) -> List a -> List a -> List a 239 | delete-firsts-by = \ eq . foldl (C (delete-by eq)) 240 | 241 | # init :: List a -> List a 242 | init = \ xs . xs () (S (zip-with K) tail xs) 243 | 244 | # last :: List a -> a 245 | last = foldl KI () 246 | 247 | # tails :: List a -> List (List a) 248 | tails = \ xs . cons xs (xs nil (K tails)) 249 | 250 | # inits :: List a -> List (List a) 251 | inits = \ xs . xs (singleton nil) \ x xs . cons nil (map (cons x) (inits xs)) 252 | 253 | # slice :: Number -> Number -> List a -> List a 254 | slice = \ i j xs . le i j nil (take (sub j i) (drop i xs)) 255 | 256 | # transpose :: List (List a) -> List (List a) 257 | transpose = \ xss . xss nil 258 | \ ys yss . ys (transpose yss) 259 | (unzip (map-option uncons xss) \ xs xss . cons xs (transpose xss)) 260 | -------------------------------------------------------------------------------- /tests/scott-lists/test.js: -------------------------------------------------------------------------------- 1 | import {readFileSync} from "fs"; 2 | import {assert, config as chaiConfig} from "chai"; 3 | chaiConfig.truncateThreshold = 0; 4 | 5 | import * as LC from "../../src/lambda-calculus.js"; 6 | LC.configure({ purity: "LetRec", numEncoding: "Scott", verbosity: "Concise" }); 7 | 8 | const solutionText = readFileSync(new URL("./solution.txt", import.meta.url), {encoding: "utf8"}); 9 | const solution = LC.compile(solutionText); 10 | 11 | const {nil,cons,singleton} = solution; 12 | const {foldr,foldl,scanr,scanl} = solution; 13 | const {take,drop} = solution; 14 | const {append,concat,snoc,uncons} = solution; 15 | const {iterate,repeat,cycle,replicate,unfold} = solution; 16 | const {head,tail,"null":isNil,length,sum,product} = solution; 17 | const {map,"concat-map":concatMap,filter} = solution; 18 | const {"take-while":takeWhile,"drop-while":dropWhile,"drop-while-end":dropWhileEnd} = solution; 19 | const {"split-at":splitAt,get,set} = solution; 20 | const {any,all,find,"find-indices":findIndices,"find-index":findIndex} = solution; 21 | const {partition,span,"minimum-by":minimumBy,"maximum-by":maximumBy} = solution; 22 | const {"insert-by":insertBy,"sort-by":sortBy,reverse} = solution; 23 | const {"zip-with":zipWith,zip,unzip} = solution; 24 | const {"group-by":groupBy,"nub-by":nubBy,"delete-by":deleteBy,"delete-firsts-by":deleteFirstsBy} = solution; 25 | const {init,last,tails,inits,slice,transpose} = solution; 26 | const {zero,succ,pred,add,"is-zero":isZero,Pair,None,Some} = solution; 27 | 28 | const { fromInt, toInt } = LC; 29 | const fromArray = xs => xs.reduceRight( (z,x) => cons(x)(z) , nil ) ; 30 | const toArray = foldl ( z => x => [...z,x] ) ([]) ; 31 | const fromPair = ([fst,snd]) => Pair(fst)(snd) ; 32 | const toPair = xy => xy ( fst => snd => [fst,snd] ) ; 33 | const fromNullable = x => x===null ? None : Some(x) ; 34 | const toNullable = fn => optX => optX (null) (fn) ; 35 | 36 | const rnd = (m,n=0) => Math.random() * (n-m) + m | 0 ; 37 | const elements = xs => xs[ rnd(xs.length) ] ; 38 | const rndArray = size => Array.from( { length: rnd(size) }, () => rnd(size) ) ; 39 | const rndNonEmptyArray = size => Array.from( { length: rnd(size) || 1 }, () => rnd(size) ) ; 40 | 41 | describe("Scott Lists",function(){ 42 | it("nil,cons,singleton",()=>{ 43 | LC.configure({ purity: "LetRec", numEncoding: "Scott", verbosity: "Concise" }); 44 | assert.deepEqual( toArray( nil ), [] ); 45 | for ( let i=1; i<=10; i++ ) { 46 | const x = rnd(i), xs = rndArray(i); 47 | assert.deepEqual( toArray( cons (x) (fromArray(xs)) ).map(toInt), [x,...xs], `after ${ i } tests` ); 48 | assert.deepEqual( toArray( singleton (x) ).map(toInt), [x], `after ${ i } tests` ); 49 | } 50 | }); 51 | it("foldr,foldl,scanr,scanl",()=>{ 52 | for ( let i=1; i<=10; i++ ) { 53 | const xs = rndArray(i); 54 | assert.equal( foldr (add) (zero) (fromArray(xs)), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` ); 55 | assert.equal( foldl (add) (zero) (fromArray(xs)), xs.reduce((x,y)=>x+y,0), `after ${ i } tests` ); 56 | assert.deepEqual( toArray( scanr (add) (zero) (fromArray(xs)) ).map(toInt), xs.reduceRight( (z,x) => [ z[0]+x, ...z ], [0] ), `after ${ i } tests` ); 57 | assert.deepEqual( toArray( scanl (add) (zero) (fromArray(xs)) ).map(toInt), xs.reduce( (z,x) => [ ...z, z[z.length-1]+x ] , [0] ), `after ${ i } tests` ); 58 | } 59 | }); 60 | it("take,drop",()=>{ 61 | for ( let i=1; i<=10; i++ ) { 62 | const n = rnd(i), xs = rndArray(i); 63 | assert.deepEqual( toArray( take (n) (fromArray(xs)) ).map(toInt), xs.slice(0,n), `after ${ i } tests` ); 64 | assert.deepEqual( toArray( drop (n) (fromArray(xs)) ).map(toInt), xs.slice(n), `after ${ i } tests` ); 65 | } 66 | }); 67 | it("append,concat,snoc",()=>{ 68 | for ( let i=1; i<=10; i++ ) { 69 | const x = rnd(i), xs = rndArray(i), ys = rndArray(i); 70 | assert.deepEqual( toArray( append (fromArray(xs)) (fromArray(ys)) ).map(toInt), [...xs,...ys], `after ${ i } tests` ); 71 | assert.deepEqual( toArray( concat (fromArray([ fromArray(xs), fromArray(ys) ])) ).map(toInt), [...xs,...ys], `after ${ i } tests` ); 72 | assert.deepEqual( toArray( snoc (fromArray(xs)) (x) ).map(toInt), [...xs,x], `after ${ i } tests` ); 73 | } 74 | }); 75 | it("uncons",()=>{ 76 | for ( let i=1; i<=10; i++ ) { 77 | const xs = rndArray(i); 78 | assert.deepEqual( toNullable ( xy => toPair(xy).map( (x,i) => i ? toArray(x).map(toInt) : toInt(x) ) ) ( uncons (fromArray(xs)) ), 79 | xs.length ? [ xs[0], xs.slice(1) ] : null , 80 | `after ${ i } tests` ); 81 | } 82 | }); 83 | it("iterate",()=>{ 84 | const N = 10; 85 | for ( let i=1; i<=10; i++ ) { 86 | const x = rnd(i), y = rnd(i); 87 | const actual = toArray( take (N) (iterate (add (y)) (x)) ).map(toInt); 88 | const expected = Array.from( { length: N }, (_,i) => x + i * y ); 89 | assert.deepEqual( actual, expected, `after ${ i } tests` ); 90 | } 91 | }); 92 | it("repeat",()=>{ 93 | const N = 10; 94 | for ( let i=1; i<=10; i++ ) { 95 | const x = rnd(i); 96 | const actual = toArray( take (N) (repeat (x)) ).map(toInt); 97 | const expected = Array.from( { length: N }, () => x ); 98 | assert.deepEqual( actual, expected, `after ${ i } tests` ); 99 | } 100 | }); 101 | it("cycle",()=>{ 102 | const N = 10; 103 | for ( let i=1; i<=10; i++ ) { 104 | const xs = rndNonEmptyArray(i); 105 | const actual = toArray( take (N) (cycle (fromArray(xs))) ).map(toInt); 106 | const expected = [].concat(...Array.from( { length: N }, () => xs )).slice(0,N); 107 | assert.deepEqual( actual, expected, `after ${ i } tests` ); 108 | } 109 | }); 110 | it("replicate",()=>{ 111 | for ( let i=1; i<=10; i++ ) { 112 | const n = rnd(i), x = rnd(i); 113 | const actual = toArray( replicate (n) (x) ).map(toInt); 114 | const expected = Array.from( { length: n }, () => x ); 115 | assert.deepEqual( actual, expected, `after ${ i } tests` ); 116 | } 117 | }); 118 | it("unfold",()=>{ 119 | for ( let i=1; i<=10; i++ ) { 120 | const x = rnd(i); 121 | const actual = toArray( unfold ( x => (isZero (x)) (Some (Pair (x) (pred (x)))) (None) ) (x) ).map(toInt); 122 | const expected = Array.from( { length: x }, (_,i) => x-i ); 123 | assert.deepEqual( actual, expected, `after ${ i } tests` ); 124 | } 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /workspace/README.md: -------------------------------------------------------------------------------- 1 | The `/workspace` directory where the submitted solution is tested in. 2 | 3 | ```text 4 | ├─ files.js a wrapper module to load submitted solution and preloaded 5 | ├─ preloaded.lc optional preloaded file 6 | ├─ solution.lc submitted solution file 7 | └─ test.js tests 8 | ``` 9 | 10 | See `Dockerfile` and `examples/`. 11 | -------------------------------------------------------------------------------- /workspace/lc-test.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "fs"; 2 | 3 | import { assert, config } from "chai"; 4 | import * as LC from "@codewars/lambda-calculus"; 5 | 6 | export { assert, config, LC }; 7 | 8 | const read = (path) => readFileSync(new URL(path, import.meta.url), {encoding: "utf8"}); 9 | 10 | /** Return the contents of the solution file */ 11 | export const getSolution = () => read("./solution.lc"); 12 | 13 | /** Return the contents of the optional preloaded file */ 14 | export const getPreloaded = () => read("./preloaded.lc"); 15 | 16 | /** Return the contents of the preloaded file and the solution file combined */ 17 | export const getSolutionWithPreloaded = () => getPreloaded() + "\n_ = ()\n" + getSolution(); 18 | 19 | 20 | /** Custom assertions */ 21 | 22 | function numEql(got, exp, msg) { 23 | if ( got?.term && got?.env ) got = LC.toInt(got); 24 | if ( exp?.term && exp?.env ) exp = LC.toInt(exp); 25 | return this.equal(got, exp, msg); 26 | } 27 | 28 | Object.assign(assert, { 29 | numEql 30 | }); 31 | -------------------------------------------------------------------------------- /workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@codewars/lc-challenge", 3 | "private": true, 4 | "version": "1.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "mocha": { 10 | "reporter": "@codewars/mocha-reporter", 11 | "timeout": 0 12 | }, 13 | "dependencies": { 14 | "@codewars/lambda-calculus": "^1.1.0" 15 | }, 16 | "devDependencies": { 17 | "@codewars/mocha-reporter": "^1.0.0", 18 | "chai": "^4.3.6", 19 | "mocha": "^9.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /workspace/solution.lc: -------------------------------------------------------------------------------- 1 | # Kacarott 2 | 3 | true = \ a b . a 4 | false = \ a b . b 5 | 6 | zero = false 7 | succ = \ n f x . f (n f x) 8 | 9 | y = \ f . (\ x . f (x x)) (\ x . f (x x)) 10 | 11 | counter = y (\ count n b . b (count (succ n)) (n) ) zero 12 | -------------------------------------------------------------------------------- /workspace/test.js: -------------------------------------------------------------------------------- 1 | import { assert, LC, getSolution } from "./lc-test.js"; 2 | 3 | LC.configure({purity: "Let", numEncoding: "Church"}); 4 | const { counter } = LC.compile(getSolution()); 5 | 6 | const T = t => _ => t; 7 | const F = _ => f => f; 8 | 9 | describe("counter", () => { 10 | it("fixed tests", () => { 11 | assert.numEql(counter(T)(T)(T)(F), 3); 12 | assert.numEql(counter(T)(F), 1); 13 | assert.numEql(counter(T)(T)(T)(T)(T)(T)(T)(F), 7); 14 | }); 15 | }); 16 | --------------------------------------------------------------------------------