├── .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 | 
4 | 
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 |
4 |
--------------------------------------------------------------------------------
/logo/logo-white.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codewars/lambda-calculus/3993e3e9d5285cfa00b22c32df12a1e031cb2226/logo/logo.png
--------------------------------------------------------------------------------
/logo/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 |
--------------------------------------------------------------------------------