summaryrefslogtreecommitdiffstats
path: root/llparse
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--llparse-builder/.gitignore3
-rw-r--r--llparse-builder/.travis.yml4
-rw-r--r--llparse-builder/README.md32
-rw-r--r--llparse-builder/package-lock.json1466
-rw-r--r--llparse-builder/package.json48
-rw-r--r--llparse-builder/src/builder.ts147
-rw-r--r--llparse-builder/src/code/and.ts7
-rw-r--r--llparse-builder/src/code/base.ts16
-rw-r--r--llparse-builder/src/code/creator.ts184
-rw-r--r--llparse-builder/src/code/field-value.ts9
-rw-r--r--llparse-builder/src/code/field.ts10
-rw-r--r--llparse-builder/src/code/index.ts15
-rw-r--r--llparse-builder/src/code/is-equal.ts7
-rw-r--r--llparse-builder/src/code/load.ts7
-rw-r--r--llparse-builder/src/code/match.ts7
-rw-r--r--llparse-builder/src/code/mul-add.ts28
-rw-r--r--llparse-builder/src/code/or.ts7
-rw-r--r--llparse-builder/src/code/span.ts5
-rw-r--r--llparse-builder/src/code/store.ts7
-rw-r--r--llparse-builder/src/code/test.ts7
-rw-r--r--llparse-builder/src/code/update.ts7
-rw-r--r--llparse-builder/src/code/value.ts7
-rw-r--r--llparse-builder/src/edge.ts54
-rw-r--r--llparse-builder/src/loop-checker/index.ts205
-rw-r--r--llparse-builder/src/loop-checker/lattice.ts115
-rw-r--r--llparse-builder/src/node/base.ts96
-rw-r--r--llparse-builder/src/node/consume.ts19
-rw-r--r--llparse-builder/src/node/error.ts24
-rw-r--r--llparse-builder/src/node/index.ts8
-rw-r--r--llparse-builder/src/node/invoke.ts39
-rw-r--r--llparse-builder/src/node/match.ts162
-rw-r--r--llparse-builder/src/node/pause.ts25
-rw-r--r--llparse-builder/src/node/span-end.ts19
-rw-r--r--llparse-builder/src/node/span-start.ts16
-rw-r--r--llparse-builder/src/property.ts12
-rw-r--r--llparse-builder/src/reachability.ts31
-rw-r--r--llparse-builder/src/span-allocator.ts182
-rw-r--r--llparse-builder/src/span.ts57
-rw-r--r--llparse-builder/src/transform/base.ts12
-rw-r--r--llparse-builder/src/transform/creator.ts28
-rw-r--r--llparse-builder/src/transform/index.ts3
-rw-r--r--llparse-builder/src/transform/to-lower-unsafe.ts7
-rw-r--r--llparse-builder/src/transform/to-lower.ts7
-rw-r--r--llparse-builder/src/utils.ts19
-rw-r--r--llparse-builder/test/builder-test.ts94
-rw-r--r--llparse-builder/test/loop-checker-test.ts118
-rw-r--r--llparse-builder/test/span-allocator-test.ts146
-rw-r--r--llparse-builder/tsconfig.json15
-rw-r--r--llparse-builder/tslint.json14
-rw-r--r--llparse-frontend/.gitignore2
-rw-r--r--llparse-frontend/.travis.yml6
-rw-r--r--llparse-frontend/README.md30
-rw-r--r--llparse-frontend/package-lock.json1516
-rw-r--r--llparse-frontend/package.json43
-rw-r--r--llparse-frontend/src/code/and.ts8
-rw-r--r--llparse-frontend/src/code/base.ts8
-rw-r--r--llparse-frontend/src/code/external.ts7
-rw-r--r--llparse-frontend/src/code/field-value.ts13
-rw-r--r--llparse-frontend/src/code/field.ts8
-rw-r--r--llparse-frontend/src/code/index.ts15
-rw-r--r--llparse-frontend/src/code/is-equal.ts9
-rw-r--r--llparse-frontend/src/code/load.ts7
-rw-r--r--llparse-frontend/src/code/match.ts7
-rw-r--r--llparse-frontend/src/code/mul-add.ts26
-rw-r--r--llparse-frontend/src/code/or.ts8
-rw-r--r--llparse-frontend/src/code/span.ts7
-rw-r--r--llparse-frontend/src/code/store.ts7
-rw-r--r--llparse-frontend/src/code/test.ts8
-rw-r--r--llparse-frontend/src/code/update.ts8
-rw-r--r--llparse-frontend/src/code/value.ts7
-rw-r--r--llparse-frontend/src/container/index.ts84
-rw-r--r--llparse-frontend/src/container/wrap.ts15
-rw-r--r--llparse-frontend/src/enumerator.ts23
-rw-r--r--llparse-frontend/src/frontend.ts513
-rw-r--r--llparse-frontend/src/implementation/code.ts16
-rw-r--r--llparse-frontend/src/implementation/full.ts9
-rw-r--r--llparse-frontend/src/implementation/index.ts4
-rw-r--r--llparse-frontend/src/implementation/node.ts15
-rw-r--r--llparse-frontend/src/implementation/transform.ts9
-rw-r--r--llparse-frontend/src/namespace/frontend.ts5
-rw-r--r--llparse-frontend/src/node/base.ts46
-rw-r--r--llparse-frontend/src/node/consume.ts8
-rw-r--r--llparse-frontend/src/node/empty.ts4
-rw-r--r--llparse-frontend/src/node/error.ts9
-rw-r--r--llparse-frontend/src/node/index.ts13
-rw-r--r--llparse-frontend/src/node/invoke.ts39
-rw-r--r--llparse-frontend/src/node/match.ts11
-rw-r--r--llparse-frontend/src/node/pause.ts4
-rw-r--r--llparse-frontend/src/node/sequence.ts44
-rw-r--r--llparse-frontend/src/node/single.ts46
-rw-r--r--llparse-frontend/src/node/slot.ts20
-rw-r--r--llparse-frontend/src/node/span-end.ts12
-rw-r--r--llparse-frontend/src/node/span-start.ts12
-rw-r--r--llparse-frontend/src/node/table-lookup.ts43
-rw-r--r--llparse-frontend/src/peephole.ts52
-rw-r--r--llparse-frontend/src/span-field.ts8
-rw-r--r--llparse-frontend/src/transform/base.ts4
-rw-r--r--llparse-frontend/src/transform/id.ts7
-rw-r--r--llparse-frontend/src/transform/index.ts4
-rw-r--r--llparse-frontend/src/transform/to-lower-unsafe.ts7
-rw-r--r--llparse-frontend/src/transform/to-lower.ts7
-rw-r--r--llparse-frontend/src/trie/empty.ts9
-rw-r--r--llparse-frontend/src/trie/index.ts136
-rw-r--r--llparse-frontend/src/trie/node.ts2
-rw-r--r--llparse-frontend/src/trie/sequence.ts9
-rw-r--r--llparse-frontend/src/trie/single.ts16
-rw-r--r--llparse-frontend/src/utils/identifier.ts32
-rw-r--r--llparse-frontend/src/utils/index.ts19
-rw-r--r--llparse-frontend/src/wrap.ts3
-rw-r--r--llparse-frontend/test/container-test.ts46
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/and.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/base.ts6
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/index.ts15
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/load.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/match.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/or.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/span.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/store.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/test.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/update.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/code/value.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/index.ts5
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/base.ts38
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/consume.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/empty.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/error.ts10
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/index.ts15
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/invoke.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/pause.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/sequence.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/single.ts18
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/span-end.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/span-start.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/transform/base.ts6
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/transform/id.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/transform/index.ts5
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts8
-rw-r--r--llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/and.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/base.ts6
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/index.ts15
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/is-equal.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/load.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/match.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/mul-add.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/or.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/span.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/store.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/test.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/update.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/code/value.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/index.ts5
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/base.ts39
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/consume.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/empty.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/error.ts10
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/index.ts15
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/invoke.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/pause.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/sequence.ts15
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/single.ts22
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/span-end.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/span-start.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/node/table-lookup.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/transform/base.ts6
-rw-r--r--llparse-frontend/test/fixtures/implementation/transform/id.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/transform/index.ts5
-rw-r--r--llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts8
-rw-r--r--llparse-frontend/test/fixtures/implementation/transform/to-lower.ts8
-rw-r--r--llparse-frontend/test/frontend-test.ts187
-rw-r--r--llparse-frontend/tsconfig.json15
-rw-r--r--llparse-frontend/tslint.json16
-rw-r--r--llparse/.gitignore4
-rw-r--r--llparse/.travis.yml6
-rw-r--r--llparse/CNAME1
-rw-r--r--llparse/CODE_OF_CONDUCT.md4
-rw-r--r--llparse/LICENSE-MIT22
-rw-r--r--llparse/README.md86
-rw-r--r--llparse/_config.yml1
-rw-r--r--llparse/examples/http/.gitignore6
-rw-r--r--llparse/examples/http/Makefile11
-rw-r--r--llparse/examples/http/index.ts51
-rw-r--r--llparse/examples/http/main.c48
-rw-r--r--llparse/package-lock.json1802
-rw-r--r--llparse/package.json49
-rw-r--r--llparse/src/api.ts47
-rw-r--r--llparse/src/compiler/header-builder.ts80
-rw-r--r--llparse/src/compiler/index.ts88
-rw-r--r--llparse/src/implementation/c/code/and.ts11
-rw-r--r--llparse/src/implementation/c/code/base.ts12
-rw-r--r--llparse/src/implementation/c/code/external.ts19
-rw-r--r--llparse/src/implementation/c/code/field.ts28
-rw-r--r--llparse/src/implementation/c/code/index.ts27
-rw-r--r--llparse/src/implementation/c/code/is-equal.ts10
-rw-r--r--llparse/src/implementation/c/code/load.ts10
-rw-r--r--llparse/src/implementation/c/code/mul-add.ts67
-rw-r--r--llparse/src/implementation/c/code/or.ts11
-rw-r--r--llparse/src/implementation/c/code/store.ts11
-rw-r--r--llparse/src/implementation/c/code/test.ts11
-rw-r--r--llparse/src/implementation/c/code/update.ts11
-rw-r--r--llparse/src/implementation/c/compilation.ts336
-rw-r--r--llparse/src/implementation/c/constants.ts45
-rw-r--r--llparse/src/implementation/c/helpers/match-sequence.ts75
-rw-r--r--llparse/src/implementation/c/index.ts199
-rw-r--r--llparse/src/implementation/c/node/base.ts77
-rw-r--r--llparse/src/implementation/c/node/consume.ts48
-rw-r--r--llparse/src/implementation/c/node/empty.ts16
-rw-r--r--llparse/src/implementation/c/node/error.ts33
-rw-r--r--llparse/src/implementation/c/node/index.ts27
-rw-r--r--llparse/src/implementation/c/node/invoke.ts44
-rw-r--r--llparse/src/implementation/c/node/pause.ts19
-rw-r--r--llparse/src/implementation/c/node/sequence.ts55
-rw-r--r--llparse/src/implementation/c/node/single.ts47
-rw-r--r--llparse/src/implementation/c/node/span-end.ts56
-rw-r--r--llparse/src/implementation/c/node/span-start.ts26
-rw-r--r--llparse/src/implementation/c/node/table-lookup.ts196
-rw-r--r--llparse/src/implementation/c/transform/base.ts10
-rw-r--r--llparse/src/implementation/c/transform/id.ts11
-rw-r--r--llparse/src/implementation/c/transform/index.ts11
-rw-r--r--llparse/src/implementation/c/transform/to-lower-unsafe.ts10
-rw-r--r--llparse/src/implementation/c/transform/to-lower.ts11
-rw-r--r--llparse/test/code-test.ts168
-rw-r--r--llparse/test/compiler-test.ts289
-rw-r--r--llparse/test/consume-test.ts69
-rw-r--r--llparse/test/fixtures/extra.c84
-rw-r--r--llparse/test/fixtures/index.ts52
-rw-r--r--llparse/test/resumption-test.ts55
-rw-r--r--llparse/test/span-test.ts107
-rw-r--r--llparse/test/transform-test.ts41
-rw-r--r--llparse/tsconfig.json15
-rw-r--r--llparse/tslint.json16
234 files changed, 12272 insertions, 0 deletions
diff --git a/llparse-builder/.gitignore b/llparse-builder/.gitignore
new file mode 100644
index 0000000..5e67ab3
--- /dev/null
+++ b/llparse-builder/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+npm-debug.log
+lib/
diff --git a/llparse-builder/.travis.yml b/llparse-builder/.travis.yml
new file mode 100644
index 0000000..b5efd79
--- /dev/null
+++ b/llparse-builder/.travis.yml
@@ -0,0 +1,4 @@
+sudo: false
+language: node_js
+node_js:
+ - "stable"
diff --git a/llparse-builder/README.md b/llparse-builder/README.md
new file mode 100644
index 0000000..522fba2
--- /dev/null
+++ b/llparse-builder/README.md
@@ -0,0 +1,32 @@
+# llparse-builder
+[![Build Status](https://secure.travis-ci.org/indutny/llparse-builder.svg)](http://travis-ci.org/indutny/llparse-builder)
+[![NPM version](https://badge.fury.io/js/llparse-builder.svg)](https://badge.fury.io/js/llparse-builder)
+
+See [llparse][0].
+
+#### LICENSE
+
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+[0]: https://github.com/indutny/llparse
diff --git a/llparse-builder/package-lock.json b/llparse-builder/package-lock.json
new file mode 100644
index 0000000..5e76f34
--- /dev/null
+++ b/llparse-builder/package-lock.json
@@ -0,0 +1,1466 @@
+{
+ "name": "llparse-builder",
+ "version": "1.5.2",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
+ },
+ "@types/mocha": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
+ "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "14.11.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz",
+ "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==",
+ "dev": true
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array.prototype.map": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
+ "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.4"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+ "dev": true
+ },
+ "binary-search": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
+ "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
+ "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
+ "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "chokidar": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+ "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.4.0"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.1.1"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true
+ },
+ "es-get-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
+ "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
+ "dev": true,
+ "requires": {
+ "es-abstract": "^1.17.4",
+ "has-symbols": "^1.0.1",
+ "is-arguments": "^1.0.4",
+ "is-map": "^2.0.1",
+ "is-set": "^2.0.1",
+ "is-string": "^1.0.5",
+ "isarray": "^2.0.5"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
+ "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
+ "dev": true
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-set": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
+ "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
+ "dev": true
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "iterate-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
+ "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
+ "dev": true
+ },
+ "iterate-value": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
+ "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
+ "dev": true,
+ "requires": {
+ "es-get-iterator": "^1.0.2",
+ "iterate-iterator": "^1.0.1"
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "log-symbols": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+ "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ }
+ }
+ },
+ "mocha": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz",
+ "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.4.2",
+ "debug": "4.1.1",
+ "diff": "4.0.2",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.1.6",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.14.0",
+ "log-symbols": "4.0.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.2",
+ "object.assign": "4.1.0",
+ "promise.allsettled": "1.0.2",
+ "serialize-javascript": "4.0.0",
+ "strip-json-comments": "3.0.1",
+ "supports-color": "7.1.0",
+ "which": "2.0.2",
+ "wide-align": "1.1.3",
+ "workerpool": "6.0.0",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "p-limit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "promise.allsettled": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
+ "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
+ "dev": true,
+ "requires": {
+ "array.prototype.map": "^1.0.1",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "iterate-value": "^1.0.0"
+ }
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "ts-node": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+ "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.17",
+ "yn": "3.1.1"
+ }
+ },
+ "tslib": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+ "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+ "dev": true
+ },
+ "tslint": {
+ "version": "5.20.1",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
+ "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.8.0",
+ "tsutils": "^2.29.0"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ }
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "typescript": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
+ "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "workerpool": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
+ "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
+ "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "decamelize": "^1.2.0",
+ "flat": "^4.1.0",
+ "is-plain-obj": "^1.1.0",
+ "yargs": "^14.2.3"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "yargs": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
+ "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^15.0.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
+ "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/llparse-builder/package.json b/llparse-builder/package.json
new file mode 100644
index 0000000..1c1ac54
--- /dev/null
+++ b/llparse-builder/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "llparse-builder",
+ "version": "1.5.2",
+ "description": "Build graph for consumption in LLParse",
+ "main": "lib/builder.js",
+ "types": "lib/builder.d.ts",
+ "files": [
+ "lib",
+ "src"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "clean": "rm -rf lib",
+ "prepare": "npm run clean && npm run build",
+ "lint": "tslint -c tslint.json src/*.ts src/**/*.ts src/**/**/*.ts test/*.ts",
+ "mocha": "mocha -r ts-node/register/type-check --reporter spec test/*-test.ts",
+ "test": "npm run mocha && npm run lint"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/indutny/llparse-builder.git"
+ },
+ "keywords": [
+ "llparse",
+ "builder",
+ "llvm",
+ "bitcode"
+ ],
+ "author": "Fedor Indutny <fedor@indutny.com> (http://darksi.de/)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/indutny/llparse-builder/issues"
+ },
+ "homepage": "https://github.com/indutny/llparse-builder#readme",
+ "devDependencies": {
+ "@types/mocha": "^8.0.3",
+ "@types/node": "^14.11.8",
+ "mocha": "^8.1.3",
+ "ts-node": "^9.0.0",
+ "tslint": "^5.20.1",
+ "typescript": "^4.0.3"
+ },
+ "dependencies": {
+ "@types/debug": "4.1.5 ",
+ "binary-search": "^1.3.6",
+ "debug": "^4.2.0"
+ }
+}
diff --git a/llparse-builder/src/builder.ts b/llparse-builder/src/builder.ts
new file mode 100644
index 0000000..a335a85
--- /dev/null
+++ b/llparse-builder/src/builder.ts
@@ -0,0 +1,147 @@
+import * as code from './code';
+import * as node from './node';
+import { Property, PropertyType } from './property';
+import { Span } from './span';
+import * as transform from './transform';
+
+export { code, node, transform, Property, PropertyType, Span };
+export { Edge } from './edge';
+export { LoopChecker } from './loop-checker';
+export { ISpanAllocatorResult, SpanAllocator } from './span-allocator';
+export { Reachability } from './reachability';
+
+/**
+ * Construct parsing graph for later use in `llparse`.
+ */
+export class Builder {
+ /**
+ * API for creating external callbacks and intrinsic operations.
+ */
+ public readonly code: code.Creator = new code.Creator();
+
+ /**
+ * API for creating character transforms for use in nodes created with
+ * `builder.node()`
+ */
+ public readonly transform: transform.Creator = new transform.Creator();
+
+ private readonly privProperties: Map<string, Property> = new Map();
+
+ // Various nodes
+
+ /**
+ * Create regular node for matching characters and sequences.
+ *
+ * @param name Node name
+ */
+ public node(name: string): node.Match {
+ return new node.Match(name);
+ }
+
+ /**
+ * Create terminal error node. Returns error code to user, and sets reason
+ * in the parser's state object.
+ *
+ * This node does not consume any bytes upon execution.
+ *
+ * @param errorCode Integer error code
+ * @param reason Error description
+ */
+ public error(errorCode: number, reason: string): node.Error {
+ return new node.Error(errorCode, reason);
+ }
+
+ /**
+ * Create invoke node that calls either external user callback or an
+ * intrinsic operation.
+ *
+ * This node does not consume any bytes upon execution.
+ *
+ * NOTE: When `.invoke()` is a target of `node().select()` - callback must
+ * have signature that accepts `.select()`'s value, otherwise it must be of
+ * the signature that takes no such value.
+ *
+ * @param fn Code instance to invoke
+ * @param map Object with integer keys and `Node` values. Describes
+ * nodes that are visited upon receiving particular
+ * return integer value
+ * @param otherwise Convenience `Node` argument. Effect is the same as calling
+ * `p.invoke(...).otherwise(node)`
+ */
+ public invoke(fn: code.Code, map?: node.IInvokeMap | node.Node,
+ otherwise?: node.Node): node.Invoke {
+ let res: node.Invoke;
+
+ // `.invoke(name)`
+ if (map === undefined) {
+ res = new node.Invoke(fn, {});
+ // `.invoke(name, otherwise)`
+ } else if (map instanceof node.Node) {
+ res = new node.Invoke(fn, {});
+ otherwise = map;
+ } else {
+ res = new node.Invoke(fn, map as node.IInvokeMap);
+ }
+
+ if (otherwise !== undefined) {
+ res.otherwise(otherwise);
+ }
+ return res;
+ }
+
+ /**
+ * Create node that consumes number of bytes specified by value of the
+ * state's property with name in `field` argument.
+ *
+ * @param field Property name to use
+ */
+ public consume(field: string): node.Consume {
+ return new node.Consume(field);
+ }
+
+ /**
+ * Create non-terminal node that returns `errorCode` as error number to
+ * user, but still allows feeding more data to the parser.
+ *
+ * This node does not consume any bytes upon execution.
+ *
+ * @param errorCode Integer error code
+ * @param reason Error description
+ */
+ public pause(errorCode: number, reason: string): node.Pause {
+ return new node.Pause(errorCode, reason);
+ }
+
+ // Span
+
+ /**
+ * Create Span with given `callback`.
+ *
+ * @param callback External span callback, must be result of
+ * `.code.span(...)`
+ */
+ public span(callback: code.Span): Span {
+ return new Span(callback);
+ }
+
+ // Custom property API
+
+ /**
+ * Allocate space for property in parser's state.
+ */
+ public property(ty: PropertyType, name: string): void {
+ if (this.privProperties.has(name)) {
+ throw new Error(`Duplicate property with a name: "${name}"`);
+ }
+
+ const prop = new Property(ty, name);
+ this.privProperties.set(name, prop);
+ }
+
+ /**
+ * Return list of all allocated properties in parser's state.
+ */
+ public get properties(): ReadonlyArray<Property> {
+ return Array.from(this.privProperties.values());
+ }
+}
diff --git a/llparse-builder/src/code/and.ts b/llparse-builder/src/code/and.ts
new file mode 100644
index 0000000..5f78675
--- /dev/null
+++ b/llparse-builder/src/code/and.ts
@@ -0,0 +1,7 @@
+import { FieldValue } from './field-value';
+
+export class And extends FieldValue {
+ constructor(field: string, value: number) {
+ super('match', 'and', field, value);
+ }
+}
diff --git a/llparse-builder/src/code/base.ts b/llparse-builder/src/code/base.ts
new file mode 100644
index 0000000..00b479f
--- /dev/null
+++ b/llparse-builder/src/code/base.ts
@@ -0,0 +1,16 @@
+export type Signature = 'match' | 'value';
+
+/**
+ * Base code class.
+ */
+export abstract class Code {
+ /**
+ * @param signature Code signature to be used. `match` means that code takes
+ * no input value (from `.select()`), otherwise it must be
+ * `value`
+ * @param name External function or intrinsic name.
+ */
+ constructor(public readonly signature: Signature,
+ public readonly name: string) {
+ }
+}
diff --git a/llparse-builder/src/code/creator.ts b/llparse-builder/src/code/creator.ts
new file mode 100644
index 0000000..98f9296
--- /dev/null
+++ b/llparse-builder/src/code/creator.ts
@@ -0,0 +1,184 @@
+import * as code from './';
+
+/**
+ * API for creating external callbacks and intrinsic operations.
+ */
+export class Creator {
+ // Callbacks to external C functions
+
+ /**
+ * Create an external callback that **has no** `value` argument.
+ *
+ * This callback can be used in all `Invoke` nodes except those that are
+ * targets of `.select()` method.
+ *
+ * C signature of callback must be:
+ *
+ * ```c
+ * int name(llparse_t* state, const char* p, const char* endp)
+ * ```
+ *
+ * Where `llparse_t` is parser state's type name.
+ *
+ * @param name External function name.
+ */
+ public match(name: string): code.Match {
+ return new code.Match(name);
+ }
+
+ /**
+ * Create an external callback that **has** `value` argument.
+ *
+ * This callback can be used only in `Invoke` nodes that are targets of
+ * `.select()` method.
+ *
+ * C signature of callback must be:
+ *
+ * ```c
+ * int name(llparse_t* state, const char* p, const char* endp, int value)
+ * ```
+ *
+ * Where `llparse_t` is parser state's type name.
+ *
+ * @param name External function name.
+ */
+ public value(name: string): code.Value {
+ return new code.Value(name);
+ }
+
+ /**
+ * Create an external span callback.
+ *
+ * This callback can be used only in `Span` constructor.
+ *
+ * C signature of callback must be:
+ *
+ * ```c
+ * int name(llparse_t* state, const char* p, const char* endp)
+ * ```
+ *
+ * NOTE: non-zero return value is treated as resumable error.
+ *
+ * @param name External function name.
+ */
+ public span(name: string): code.Span {
+ return new code.Span(name);
+ }
+
+ // Helpers
+
+ /**
+ * Intrinsic operation. Stores `value` from `.select()` node into the state's
+ * property with the name specified by `field`, returns zero.
+ *
+ * state[field] = value;
+ * return 0;
+ *
+ * @param field Property name
+ */
+ public store(field: string): code.Store {
+ return new code.Store(field);
+ }
+
+ /**
+ * Intrinsic operation. Loads and returns state's property with the name
+ * specified by `field`.
+ *
+ * The value of the property is either truncated or zero-extended to fit into
+ * 32-bit unsigned integer.
+ *
+ * return state[field];
+ *
+ * @param field Property name.
+ */
+ public load(field: string): code.Load {
+ return new code.Load(field);
+ }
+
+ /**
+ * Intrinsic operation. Takes `value` from `.select()`, state's property
+ * with the name `field` and does:
+ *
+ * field = state[field];
+ * field *= options.base;
+ * field += value;
+ * state[field] = field;
+ * return 0; // or 1 on overflow
+ *
+ * Return values are:
+ *
+ * - 0 - success
+ * - 1 - overflow
+ *
+ * @param field Property name
+ * @param options See `code.MulAdd` documentation.
+ */
+ public mulAdd(field: string, options: code.IMulAddOptions): code.MulAdd {
+ return new code.MulAdd(field, options);
+ }
+
+ /**
+ * Intrinsic operation. Puts `value` integer into the state's property with
+ * the name specified by `field`.
+ *
+ * state[field] = value;
+ * return 0;
+ *
+ * @param field Property name
+ * @param value Integer value to be stored into the property.
+ */
+ public update(field: string, value: number): code.Update {
+ return new code.Update(field, value);
+ }
+
+ /**
+ * Intrinsic operation. Returns 1 if the integer `value` is equal to the
+ * state's property with the name specified by `field`.
+ *
+ * return state[field] === value ? 1 : 0;
+ *
+ * @param field Property name
+ * @param value Integer value to be checked against.
+ */
+ public isEqual(field: string, value: number): code.IsEqual {
+ return new code.IsEqual(field, value);
+ }
+
+ /**
+ * Intrinsic operation.
+ *
+ * state[field] &= value
+ * return 0;
+ *
+ * @param field Property name
+ * @param value Integer value
+ */
+ public and(field: string, value: number): code.And {
+ return new code.And(field, value);
+ }
+
+ /**
+ * Intrinsic operation.
+ *
+ * state[field] |= value
+ * return 0;
+ *
+ * @param field Property name
+ * @param value Integer value
+ */
+ public or(field: string, value: number): code.Or {
+ return new code.Or(field, value);
+ }
+
+ /**
+ * Intrinsic operation.
+ *
+ * return (state[field] & value) == value ? 1 : 0;
+ *
+ * @param field Property name
+ * @param value Integer value
+ */
+ public test(field: string, value: number): code.Test {
+ return new code.Test(field, value);
+ }
+}
diff --git a/llparse-builder/src/code/field-value.ts b/llparse-builder/src/code/field-value.ts
new file mode 100644
index 0000000..2ceea69
--- /dev/null
+++ b/llparse-builder/src/code/field-value.ts
@@ -0,0 +1,9 @@
+import { Signature } from './base';
+import { Field } from './field';
+
+export abstract class FieldValue extends Field {
+ constructor(signature: Signature, name: string, field: string,
+ public readonly value: number) {
+ super(signature, name, field);
+ }
+}
diff --git a/llparse-builder/src/code/field.ts b/llparse-builder/src/code/field.ts
new file mode 100644
index 0000000..af58c84
--- /dev/null
+++ b/llparse-builder/src/code/field.ts
@@ -0,0 +1,10 @@
+import * as assert from 'assert';
+import { Code, Signature } from './base';
+
+export abstract class Field extends Code {
+ constructor(signature: Signature, name: string,
+ public readonly field: string) {
+ super(signature, name + '_' + field);
+ assert(!/^_/.test(field), 'Can\'t access internal field from user code');
+ }
+}
diff --git a/llparse-builder/src/code/index.ts b/llparse-builder/src/code/index.ts
new file mode 100644
index 0000000..7a651e3
--- /dev/null
+++ b/llparse-builder/src/code/index.ts
@@ -0,0 +1,15 @@
+export { Code } from './base';
+export { Creator } from './creator';
+export { Field } from './field';
+export { FieldValue } from './field-value';
+export { IsEqual } from './is-equal';
+export { Load } from './load';
+export { Match } from './match';
+export { IMulAddOptions, MulAdd } from './mul-add';
+export { Or } from './or';
+export { And } from './and';
+export { Span } from './span';
+export { Store } from './store';
+export { Test } from './test';
+export { Update } from './update';
+export { Value } from './value';
diff --git a/llparse-builder/src/code/is-equal.ts b/llparse-builder/src/code/is-equal.ts
new file mode 100644
index 0000000..91bb957
--- /dev/null
+++ b/llparse-builder/src/code/is-equal.ts
@@ -0,0 +1,7 @@
+import { FieldValue } from './field-value';
+
+export class IsEqual extends FieldValue {
+ constructor(field: string, value: number) {
+ super('match', 'is_equal', field, value);
+ }
+}
diff --git a/llparse-builder/src/code/load.ts b/llparse-builder/src/code/load.ts
new file mode 100644
index 0000000..9f3df2e
--- /dev/null
+++ b/llparse-builder/src/code/load.ts
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Load extends Field {
+ constructor(field: string) {
+ super('match', 'load', field);
+ }
+}
diff --git a/llparse-builder/src/code/match.ts b/llparse-builder/src/code/match.ts
new file mode 100644
index 0000000..631376a
--- /dev/null
+++ b/llparse-builder/src/code/match.ts
@@ -0,0 +1,7 @@
+import { Code } from './base';
+
+export class Match extends Code {
+ constructor(name: string) {
+ super('match', name);
+ }
+}
diff --git a/llparse-builder/src/code/mul-add.ts b/llparse-builder/src/code/mul-add.ts
new file mode 100644
index 0000000..fd648ed
--- /dev/null
+++ b/llparse-builder/src/code/mul-add.ts
@@ -0,0 +1,28 @@
+import { Field } from './field';
+
+/**
+ * Options for `code.mulAdd()`.
+ */
+export interface IMulAddOptions {
+ /** Value to multiply the property with in the first step */
+ readonly base: number;
+
+ /**
+ * Maximum value of the property. If at any point of computation the
+ * intermediate result exceeds it - `mulAdd` returns 1 (overflow).
+ */
+ readonly max?: number;
+
+ /**
+ * If `true` - all arithmetics perfomed by `mulAdd` will be signed.
+ *
+ * Default value: `false`
+ */
+ readonly signed?: boolean;
+}
+
+export class MulAdd extends Field {
+ constructor(field: string, public readonly options: IMulAddOptions) {
+ super('value', 'mul_add', field);
+ }
+}
diff --git a/llparse-builder/src/code/or.ts b/llparse-builder/src/code/or.ts
new file mode 100644
index 0000000..33bd402
--- /dev/null
+++ b/llparse-builder/src/code/or.ts
@@ -0,0 +1,7 @@
+import { FieldValue } from './field-value';
+
+export class Or extends FieldValue {
+ constructor(field: string, value: number) {
+ super('match', 'or', field, value);
+ }
+}
diff --git a/llparse-builder/src/code/span.ts b/llparse-builder/src/code/span.ts
new file mode 100644
index 0000000..b97e09e
--- /dev/null
+++ b/llparse-builder/src/code/span.ts
@@ -0,0 +1,5 @@
+import { Match } from './match';
+
+export class Span extends Match {
+ // no-op
+}
diff --git a/llparse-builder/src/code/store.ts b/llparse-builder/src/code/store.ts
new file mode 100644
index 0000000..84abfef
--- /dev/null
+++ b/llparse-builder/src/code/store.ts
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Store extends Field {
+ constructor(field: string) {
+ super('value', 'store', field);
+ }
+}
diff --git a/llparse-builder/src/code/test.ts b/llparse-builder/src/code/test.ts
new file mode 100644
index 0000000..a9d0a22
--- /dev/null
+++ b/llparse-builder/src/code/test.ts
@@ -0,0 +1,7 @@
+import { FieldValue } from './field-value';
+
+export class Test extends FieldValue {
+ constructor(field: string, value: number) {
+ super('match', 'test', field, value);
+ }
+}
diff --git a/llparse-builder/src/code/update.ts b/llparse-builder/src/code/update.ts
new file mode 100644
index 0000000..de62476
--- /dev/null
+++ b/llparse-builder/src/code/update.ts
@@ -0,0 +1,7 @@
+import { FieldValue } from './field-value';
+
+export class Update extends FieldValue {
+ constructor(field: string, value: number) {
+ super('match', 'update', field, value);
+ }
+}
diff --git a/llparse-builder/src/code/value.ts b/llparse-builder/src/code/value.ts
new file mode 100644
index 0000000..06c6fd7
--- /dev/null
+++ b/llparse-builder/src/code/value.ts
@@ -0,0 +1,7 @@
+import { Code } from './base';
+
+export class Value extends Code {
+ constructor(name: string) {
+ super('value', name);
+ }
+}
diff --git a/llparse-builder/src/edge.ts b/llparse-builder/src/edge.ts
new file mode 100644
index 0000000..f6b55cc
--- /dev/null
+++ b/llparse-builder/src/edge.ts
@@ -0,0 +1,54 @@
+import * as assert from 'assert';
+
+import { Buffer } from 'buffer';
+import { Invoke, Node } from './node';
+
+/**
+ * This class represents an edge in the parser graph.
+ */
+export class Edge {
+ /**
+ * Comparator for `.sort()` function.
+ */
+ public static compare(a: Edge, b: Edge): number {
+ if (typeof a.key === 'number') {
+ return a.key - (b.key as number);
+ }
+ return a.key!.compare(b.key as Buffer);
+ }
+
+ /**
+ * @param node Edge target
+ * @param noAdvance If `true` - the parent should not consume bytes before
+ * moving to the target `node`
+ * @param key `Buffer` for `node.Match`, `number` for `node.Invoke`,
+ * `undefined` for edges created with `.otherwise()`
+ * @param value `.select()` value associated with the edge
+ */
+ constructor(public readonly node: Node,
+ public readonly noAdvance: boolean,
+ public readonly key: Buffer | number | undefined,
+ public readonly value: number | undefined) {
+ if (node instanceof Invoke) {
+ if (value === undefined) {
+ assert.strictEqual(node.code.signature, 'match',
+ 'Invalid Invoke\'s code signature');
+ } else {
+ assert.strictEqual(node.code.signature, 'value',
+ 'Invalid Invoke\'s code signature');
+ }
+ } else {
+ assert.strictEqual(value, undefined,
+ 'Attempting to pass value to non-Invoke node');
+ }
+
+ if (Buffer.isBuffer(key)) {
+ assert(key.length > 0, 'Invalid edge buffer length');
+
+ if (noAdvance) {
+ assert.strictEqual(key.length, 1,
+ 'Only 1-char keys are allowed in `noAdvance` edges');
+ }
+ }
+ }
+}
diff --git a/llparse-builder/src/loop-checker/index.ts b/llparse-builder/src/loop-checker/index.ts
new file mode 100644
index 0000000..5751955
--- /dev/null
+++ b/llparse-builder/src/loop-checker/index.ts
@@ -0,0 +1,205 @@
+import * as assert from 'assert';
+import * as debugAPI from 'debug';
+
+import { Node } from '../node';
+import { Reachability } from '../reachability';
+import { Lattice } from './lattice';
+
+const debug = debugAPI('llparse-builder:loop-checker');
+
+const EMPTY_VALUE = new Lattice('empty');
+const ANY_VALUE = new Lattice('any');
+
+/**
+ * This class implements a loop checker pass. The goal of this pass is to verify
+ * that the graph doesn't contain infinite loops.
+ */
+export class LoopChecker {
+ private readonly lattice: Map<Node, Lattice> = new Map();
+
+ // Just a cache of terminated keys
+ private readonly terminatedCache: Map<Node, Lattice> = new Map();
+
+ /**
+ * Run loop checker pass on a graph starting from `root`.
+ *
+ * Throws on failure.
+ *
+ * @param root Graph root node
+ */
+ public check(root: Node): void {
+ const r = new Reachability();
+
+ const nodes = r.build(root);
+
+ for (const node of nodes) {
+ debug('checking loops starting from %j', node.name);
+
+ // Set initial lattice value for all nodes
+ this.clear(nodes);
+
+ // Mark root as reachable with any value
+ this.lattice.set(node, ANY_VALUE);
+
+ // Raise lattice values
+ let changed: Set<Node> = new Set([ root ]);
+ while (changed.size !== 0) {
+ if (debug.enabled) {
+ debug('changed %j', Array.from(changed).map((other) => other.name));
+ }
+
+ const next: Set<Node> = new Set();
+ for (const changedNode of changed) {
+ this.propagate(changedNode, next);
+ }
+ changed = next;
+ }
+
+ debug('lattice stabilized');
+
+ // Visit nodes and walk through reachable edges to detect loops
+ this.visit(node, []);
+ }
+ }
+
+ private clear(nodes: ReadonlyArray<Node>): void {
+ for (const node of nodes) {
+ this.lattice.set(node, EMPTY_VALUE);
+ }
+ }
+
+ private propagate(node: Node, changed: Set<Node>): void {
+ let value: Lattice = this.lattice.get(node)!;
+ debug('propagate(%j), initial value %j', node.name, value);
+
+ // Terminate values that are consumed by `match`/`select`
+ const terminated = this.terminate(node, value, changed);
+ if (!terminated.isEqual(EMPTY_VALUE)) {
+ debug('node %j terminates %j', node.name, terminated);
+ value = value.subtract(terminated);
+ if (value.isEqual(EMPTY_VALUE)) {
+ return;
+ }
+ }
+
+ const keysByTarget: Map<Node, Lattice> = new Map();
+ // Propagate value through `.peek()`/`.otherwise()` edges
+ for (const edge of node.getAllEdges()) {
+ if (!edge.noAdvance) {
+ continue;
+ }
+
+ let targetValue: Lattice;
+ if (keysByTarget.has(edge.node)) {
+ targetValue = keysByTarget.get(edge.node)!;
+ } else {
+ targetValue = this.lattice.get(edge.node)!;
+ }
+
+ // `otherwise` or `Invoke`'s edges
+ if (edge.key === undefined || typeof edge.key === 'number') {
+ targetValue = targetValue.union(value);
+ } else {
+ // `.peek()`
+ const edgeValue = new Lattice([ edge.key[0] ]).intersect(value);
+ if (edgeValue.isEqual(EMPTY_VALUE)) {
+ continue;
+ }
+
+ targetValue = targetValue.union(edgeValue);
+ }
+
+ keysByTarget.set(edge.node, targetValue);
+ }
+
+ for (const [ child, childValue ] of keysByTarget) {
+ debug('node %j propagates %j to %j', node.name, childValue,
+ child.name);
+ this.update(child, childValue, changed);
+ }
+ }
+
+ private update(node: Node, newValue: Lattice, changed: Set<Node>): boolean {
+ const value = this.lattice.get(node)!;
+ if (newValue.isEqual(value)) {
+ return false;
+ }
+
+ this.lattice.set(node, newValue);
+ changed.add(node);
+ return true;
+ }
+
+ private terminate(node: Node, value: Lattice, changed: Set<Node>): Lattice {
+ if (this.terminatedCache.has(node)) {
+ return this.terminatedCache.get(node)!;
+ }
+
+ const terminated: number[] = [];
+ for (const edge of node.getAllEdges()) {
+ if (edge.noAdvance) {
+ continue;
+ }
+
+ // Ignore `otherwise` and `Invoke`'s edges
+ if (edge.key === undefined || typeof edge.key === 'number') {
+ continue;
+ }
+
+ terminated.push(edge.key[0]);
+ }
+
+ const result = new Lattice(terminated);
+ this.terminatedCache.set(node, result);
+ return result;
+ }
+
+ private visit(node: Node, path: ReadonlyArray<Node>): void {
+ let value = this.lattice.get(node)!;
+ debug('enter %j, value is %j', node.name, value);
+
+ const terminated = this.terminatedCache.has(node) ?
+ this.terminatedCache.get(node)! : EMPTY_VALUE;
+ if (!terminated.isEqual(EMPTY_VALUE)) {
+ debug('subtract terminated %j', terminated);
+ value = value.subtract(terminated);
+ if (value.isEqual(EMPTY_VALUE)) {
+ debug('terminated everything');
+ return;
+ }
+ }
+
+ for (const edge of node.getAllEdges()) {
+ if (!edge.noAdvance) {
+ continue;
+ }
+
+ let edgeValue = value;
+
+ // `otherwise` or `Invoke`'s edges
+ if (edge.key === undefined || typeof edge.key === 'number') {
+ // nothing to do
+ // `.peek()`
+ } else {
+ edgeValue = edgeValue.intersect(new Lattice([ edge.key[0] ]));
+ }
+
+ // Ignore unreachable edges
+ if (edgeValue.isEqual(EMPTY_VALUE)) {
+ continue;
+ }
+ if (path.indexOf(edge.node) !== -1) {
+ if (path.length === 0) {
+ throw new Error(
+ `Detected loop in "${edge.node.name}" through "${edge.node.name}"`);
+ }
+ throw new Error(
+ `Detected loop in "${edge.node.name}" through chain ` +
+ `${path.map((parent) => '"' + parent.name + '"').join(' -> ')}`);
+ }
+ this.visit(edge.node, path.concat(edge.node));
+ }
+
+ debug('leave %j', node.name);
+ }
+}
diff --git a/llparse-builder/src/loop-checker/lattice.ts b/llparse-builder/src/loop-checker/lattice.ts
new file mode 100644
index 0000000..8d2a7fe
--- /dev/null
+++ b/llparse-builder/src/loop-checker/lattice.ts
@@ -0,0 +1,115 @@
+import * as assert from 'assert';
+
+const MAX_VALUE = 256;
+const WORD_SIZE = 32;
+const SIZE = (MAX_VALUE / WORD_SIZE) | 0;
+const WORD_FILL = -1 | 0;
+
+assert.strictEqual(MAX_VALUE % WORD_SIZE, 0);
+
+export type LatticeValue = 'empty' | ReadonlyArray<number> | 'any';
+
+/**
+ * A fixed-size bitfield, really
+ */
+export class Lattice {
+ protected readonly words: number[];
+
+ constructor(value: LatticeValue) {
+ this.words = new Array(SIZE).fill(value === 'any' ? WORD_FILL : 0);
+
+ if (Array.isArray(value)) {
+ for (const single of value) {
+ this.add(single);
+ }
+ }
+ }
+
+ public check(bit: number): boolean {
+ assert(0 <= bit && bit < MAX_VALUE, 'Invalid bit');
+ const index = (bit / WORD_SIZE) | 0;
+ const off = bit % WORD_SIZE;
+ return (this.words[index] & (1 << off)) !== 0;
+ }
+
+ public union(other: Lattice): Lattice {
+ const result = new Lattice('empty');
+
+ for (let i = 0; i < SIZE; i++) {
+ result.words[i] = this.words[i] | other.words[i];
+ }
+
+ return result;
+ }
+
+ public intersect(other: Lattice): Lattice {
+ const result = new Lattice('empty');
+
+ for (let i = 0; i < SIZE; i++) {
+ result.words[i] = this.words[i] & other.words[i];
+ }
+
+ return result;
+ }
+
+ public subtract(other: Lattice): Lattice {
+ const result = new Lattice('empty');
+
+ for (let i = 0; i < SIZE; i++) {
+ result.words[i] = this.words[i] & (~other.words[i]);
+ }
+
+ return result;
+ }
+
+ public isEqual(other: Lattice): boolean {
+ if (this === other) {
+ return true;
+ }
+
+ for (let i = 0; i < SIZE; i++) {
+ if (this.words[i] !== other.words[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public *[Symbol.iterator](): Iterator<number> {
+ // TODO(indutny): improve speed if needed
+ for (let i = 0; i < MAX_VALUE; i++) {
+ if (this.check(i)) {
+ yield i;
+ }
+ }
+ }
+
+ public toJSON(): any {
+ let isEmpty = true;
+ let isFull = true;
+ for (let i = 0; i < SIZE; i++) {
+ if (this.words[i] !== 0) {
+ isEmpty = false;
+ }
+ if (this.words[i] !== WORD_FILL) {
+ isFull = false;
+ }
+ }
+ if (isEmpty) {
+ return 'empty';
+ }
+ if (isFull) {
+ return 'any';
+ }
+ return Array.from(this);
+ }
+
+ // Private
+
+ private add(bit: number): void {
+ assert(0 <= bit && bit < MAX_VALUE, 'Invalid bit');
+ const index = (bit / WORD_SIZE) | 0;
+ const off = bit % WORD_SIZE;
+ this.words[index] |= 1 << off;
+ }
+}
diff --git a/llparse-builder/src/node/base.ts b/llparse-builder/src/node/base.ts
new file mode 100644
index 0000000..9840f16
--- /dev/null
+++ b/llparse-builder/src/node/base.ts
@@ -0,0 +1,96 @@
+import * as assert from 'assert';
+import binarySearch = require('binary-search');
+import { Edge } from '../edge';
+
+/**
+ * Base class for all graph nodes.
+ */
+export abstract class Node {
+ private otherwiseEdge: Edge | undefined;
+ private privEdges: Edge[] = [];
+
+ /**
+ * @param name Node name
+ */
+ constructor(public readonly name: string) {
+ // no-op
+ }
+
+ /**
+ * Create an otherwise edge to node `node`.
+ *
+ * This edge is executed when no other edges match current input. No
+ * characters are consumed upon transition.
+ *
+ * NOTE: At most one otherwise (skipping or not) edge can be set, most nodes
+ * except `Error` require it.
+ *
+ * @param node Target node
+ */
+ public otherwise(node: Node): this {
+ if (this.otherwiseEdge !== undefined) {
+ throw new Error('Node already has `otherwise` or `skipTo`');
+ }
+
+ this.otherwiseEdge = new Edge(node, true, undefined, undefined);
+ return this;
+ }
+
+ /**
+ * Create a skipping otherwise edge to node `node`.
+ *
+ * This edge is executed when no other edges match current input. Single
+ * character is consumed upon transition.
+ *
+ * NOTE: At most one otherwise (skipping or not) edge can be set, most nodes
+ * except `Error` require it.
+ *
+ * @param node Target node
+ */
+ public skipTo(node: Node): this {
+ if (this.otherwiseEdge !== undefined) {
+ throw new Error('Node already has `otherwise` or `skipTo`');
+ }
+
+ this.otherwiseEdge = new Edge(node, false, undefined, undefined);
+ return this;
+ }
+
+ // Limited public use
+
+ /** Get otherwise edge. */
+ public getOtherwiseEdge(): Edge | undefined {
+ return this.otherwiseEdge;
+ }
+
+ /** Get list of all non-otherwise edges. */
+ public getEdges(): ReadonlyArray<Edge> {
+ return this.privEdges;
+ }
+
+ /** Get list of all edges (including otherwise, if present). */
+ public getAllEdges(): ReadonlyArray<Edge> {
+ const res = this.privEdges;
+ if (this.otherwiseEdge === undefined) {
+ return res;
+ } else {
+ return res.concat(this.otherwiseEdge);
+ }
+ }
+
+ /** Get iterator through all non-otherwise edges. */
+ public *[Symbol.iterator](): Iterator<Edge> {
+ yield* this.privEdges;
+ }
+
+ // Internal
+
+ protected addEdge(edge: Edge): void {
+ assert.notStrictEqual(edge.key, undefined);
+
+ const index = binarySearch(this.privEdges, edge, Edge.compare);
+ assert(index < 0, 'Attempting to create duplicate edge');
+
+ this.privEdges.splice(-1 - index, 0, edge);
+ }
+}
diff --git a/llparse-builder/src/node/consume.ts b/llparse-builder/src/node/consume.ts
new file mode 100644
index 0000000..eff4037
--- /dev/null
+++ b/llparse-builder/src/node/consume.ts
@@ -0,0 +1,19 @@
+import * as assert from 'assert';
+import { Node } from './base';
+
+/**
+ * This node consumes number of characters specified by state's property with
+ * name `field` from the input, and forwards execution to `otherwise` node.
+ */
+export class Consume extends Node {
+ /**
+ * @param field State's property name
+ */
+ constructor(public readonly field: string) {
+ super('consume_' + field);
+
+ if (/^_/.test(field)) {
+ throw new Error(`Can't use internal field in \`consume()\`: "${field}"`);
+ }
+ }
+}
diff --git a/llparse-builder/src/node/error.ts b/llparse-builder/src/node/error.ts
new file mode 100644
index 0000000..393f566
--- /dev/null
+++ b/llparse-builder/src/node/error.ts
@@ -0,0 +1,24 @@
+import * as assert from 'assert';
+import { Node } from './base';
+
+/**
+ * This node terminates the execution with an error
+ */
+class NodeError extends Node {
+ /**
+ * @param code Error code to return to user
+ * @param reason Error description to store in parser's state
+ */
+ constructor(public readonly code: number, public readonly reason: string) {
+ super('error');
+ assert.strictEqual(code, code | 0, 'code must be integer');
+ }
+
+ /** `.otherwise()` is not supported on this type of node */
+ public otherwise(node: Node): this { throw new Error('Not supported'); }
+
+ /** `.skipTo()` is not supported on this type of node */
+ public skipTo(node: Node): this { throw new Error('Not supported'); }
+}
+
+export { NodeError as Error };
diff --git a/llparse-builder/src/node/index.ts b/llparse-builder/src/node/index.ts
new file mode 100644
index 0000000..e3d5fe5
--- /dev/null
+++ b/llparse-builder/src/node/index.ts
@@ -0,0 +1,8 @@
+export { Node } from './base';
+export { Consume } from './consume';
+export { Error } from './error';
+export { Invoke, IInvokeMap } from './invoke';
+export { Match } from './match';
+export { Pause } from './pause';
+export { SpanStart } from './span-start';
+export { SpanEnd } from './span-end';
diff --git a/llparse-builder/src/node/invoke.ts b/llparse-builder/src/node/invoke.ts
new file mode 100644
index 0000000..d6791a7
--- /dev/null
+++ b/llparse-builder/src/node/invoke.ts
@@ -0,0 +1,39 @@
+import * as assert from 'assert';
+
+import { Code } from '../code';
+import { Edge } from '../edge';
+import { Node } from './base';
+
+/**
+ * Map of return codes of the callback. Each key is a return code,
+ * value is the target node that must be executed upon getting such return code.
+ */
+export interface IInvokeMap {
+ readonly [key: number]: Node;
+}
+
+/**
+ * This node invokes either external callback or intrinsic code and passes the
+ * execution to either a target from a `map` (if the return code matches one of
+ * registered in it), or to `otherwise` node.
+ */
+export class Invoke extends Node {
+ /**
+ * @param code External callback or intrinsic code. Can be created with
+ * `builder.code.*()` methods.
+ * @param map Map from callback return codes to target nodes
+ */
+ constructor(public readonly code: Code, map: IInvokeMap) {
+ super('invoke_' + code.name);
+
+ Object.keys(map).forEach((mapKey) => {
+ const numKey: number = parseInt(mapKey, 10);
+ const targetNode = map[numKey]!;
+
+ assert.strictEqual(numKey, numKey | 0,
+ 'Invoke\'s map keys must be integers');
+
+ this.addEdge(new Edge(targetNode, true, numKey, undefined));
+ });
+ }
+}
diff --git a/llparse-builder/src/node/match.ts b/llparse-builder/src/node/match.ts
new file mode 100644
index 0000000..617a659
--- /dev/null
+++ b/llparse-builder/src/node/match.ts
@@ -0,0 +1,162 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+
+import { Edge } from '../edge';
+import { Transform } from '../transform';
+import { toBuffer } from '../utils';
+import { Node } from './base';
+
+/**
+ * Character/sequence to match.
+ *
+ * May have following types:
+ *
+ * * `number` - for single character
+ * * `string` - for printable character sequence
+ * * `Buffer` - for raw byte sequence
+ */
+export type MatchSingleValue = string | number | Buffer;
+
+/**
+ * Convenience type for passing several characters/sequences to match methods.
+ */
+export type MatchValue = MatchSingleValue | ReadonlyArray<MatchSingleValue>;
+
+/**
+ * A map from characters/sequences to `.select()`'s values. Used for specifying
+ * the value to be passed to `.select()'`s targets.
+ */
+export interface IMatchSelect {
+ readonly [key: string]: number;
+}
+
+/**
+ * This node matches characters/sequences and forwards the execution according
+ * to matched character with optional attached value (See `.select()`).
+ */
+export class Match extends Node {
+ private transformFn: Transform | undefined;
+
+ /**
+ * Set character transformation function.
+ *
+ * @param transform Transformation to apply. Can be created with
+ * `builder.transform.*()` methods.
+ */
+ public transform(transformFn: Transform): this {
+ this.transformFn = transformFn;
+ return this;
+ }
+
+ /**
+ * Match sequence/character and forward execution to `next` on success,
+ * consuming matched bytes of the input.
+ *
+ * No value is attached on such execution forwarding, and the target node
+ * **must not** be an `Invoke` node with a callback expecting the value.
+ *
+ * @param value Sequence/character to be matched
+ * @param next Target node to be executed on success.
+ */
+ public match(value: MatchValue, next: Node): this {
+ if (Array.isArray(value)) {
+ for (const subvalue of value) {
+ this.match(subvalue, next);
+ }
+ return this;
+ }
+
+ const buffer = toBuffer(value as MatchSingleValue);
+ const edge = new Edge(next, false, buffer, undefined);
+ this.addEdge(edge);
+ return this;
+ }
+
+ /**
+ * Match character and forward execution to `next` on success
+ * without consuming one byte of the input.
+ *
+ * No value is attached on such execution forwarding, and the target node
+ * **must not** be an `Invoke` with a callback expecting the value.
+ *
+ * @param value Character to be matched
+ * @param next Target node to be executed on success.
+ */
+ public peek(value: MatchValue, next: Node): this {
+ if (Array.isArray(value)) {
+ for (const subvalue of value) {
+ this.peek(subvalue, next);
+ }
+ return this;
+ }
+
+ const buffer = toBuffer(value as MatchSingleValue);
+ assert.strictEqual(buffer.length, 1,
+ '`.peek()` accepts only single character keys');
+
+ const edge = new Edge(next, true, buffer, undefined);
+ this.addEdge(edge);
+ return this;
+ }
+
+ /**
+ * Match character/sequence and forward execution to `next` on success
+ * consumed matched bytes of the input.
+ *
+ * Value is attached on such execution forwarding, and the target node
+ * **must** be an `Invoke` with a callback expecting the value.
+ *
+ * Possible signatures:
+ *
+ * * `.select(key, value [, next ])`
+ * * `.select({ key: value } [, next])`
+ *
+ * @param keyOrMap Either a sequence to match, or a map from sequences to
+ * values
+ * @param valueOrNext Either an integer value to be forwarded to the target
+ * node, or an otherwise node
+ * @param next Convenience param. Same as calling `.otherwise(...)`
+ */
+ public select(keyOrMap: MatchSingleValue | IMatchSelect,
+ valueOrNext?: number | Node, next?: Node): this {
+ // .select({ key: value, ... }, next)
+ if (typeof keyOrMap === 'object') {
+ assert(valueOrNext instanceof Node,
+ 'Invalid `next` argument of `.select()`');
+ assert.strictEqual(next, undefined,
+ 'Invalid argument count of `.select()`');
+
+ const map: IMatchSelect = keyOrMap as IMatchSelect;
+ next = valueOrNext as Node | undefined;
+
+ Object.keys(map).forEach((mapKey) => {
+ const numKey: number = mapKey as any;
+
+ this.select(numKey, map[numKey]!, next);
+ });
+ return this;
+ }
+
+ // .select(key, value, next)
+ assert.strictEqual(typeof valueOrNext, 'number',
+ 'Invalid `value` argument of `.select()`');
+ assert.notStrictEqual(next, undefined,
+ 'Invalid `next` argument of `.select()`');
+
+ const key = toBuffer(keyOrMap as MatchSingleValue);
+ const value = valueOrNext as number;
+
+ const edge = new Edge(next!, false, key, value);
+ this.addEdge(edge);
+ return this;
+ }
+
+ // Limited public use
+
+ /**
+ * Get tranformation function
+ */
+ public getTransform(): Transform | undefined {
+ return this.transformFn;
+ }
+}
diff --git a/llparse-builder/src/node/pause.ts b/llparse-builder/src/node/pause.ts
new file mode 100644
index 0000000..2dcf5d1
--- /dev/null
+++ b/llparse-builder/src/node/pause.ts
@@ -0,0 +1,25 @@
+import * as assert from 'assert';
+import { Node } from './base';
+
+/**
+ * This returns the specified error code, but makes the resumption to
+ * `otherwise` target possible.
+ */
+export class Pause extends Node {
+ /**
+ * @param code Error code to return
+ * @param reason Error description
+ */
+ constructor(public readonly code: number, public readonly reason: string) {
+ super('pause');
+ assert.strictEqual(code, code | 0, 'code must be integer');
+ }
+
+ /**
+ * `.skipTo()` is not supported on this type of node, please use
+ * `.otherwise()`
+ */
+ public skipTo(node: Node): this {
+ throw new Error('Not supported, please use `pause.otherwise()`');
+ }
+}
diff --git a/llparse-builder/src/node/span-end.ts b/llparse-builder/src/node/span-end.ts
new file mode 100644
index 0000000..377cd73
--- /dev/null
+++ b/llparse-builder/src/node/span-end.ts
@@ -0,0 +1,19 @@
+import { Span } from '../span';
+import { Node } from './base';
+
+/**
+ * Indicates span end.
+ *
+ * A callback will be invoked with all input data since the most recent of:
+ *
+ * * Span start invocation
+ * * Parser execution
+ */
+export class SpanEnd extends Node {
+ /**
+ * @param span Span instance
+ */
+ constructor(public readonly span: Span) {
+ super(`span_end_${span.callback.name}`);
+ }
+}
diff --git a/llparse-builder/src/node/span-start.ts b/llparse-builder/src/node/span-start.ts
new file mode 100644
index 0000000..f81b432
--- /dev/null
+++ b/llparse-builder/src/node/span-start.ts
@@ -0,0 +1,16 @@
+import { Span } from '../span';
+import { Node } from './base';
+
+/**
+ * Indicates span start.
+ *
+ * See `SpanEnd` for details on callback invocation.
+ */
+export class SpanStart extends Node {
+ /**
+ * @param span Span instance
+ */
+ constructor(public readonly span: Span) {
+ super(`span_start_${span.callback.name}`);
+ }
+}
diff --git a/llparse-builder/src/property.ts b/llparse-builder/src/property.ts
new file mode 100644
index 0000000..cf2fe4b
--- /dev/null
+++ b/llparse-builder/src/property.ts
@@ -0,0 +1,12 @@
+export type PropertyType = 'i8' | 'i16' | 'i32' | 'i64' | 'ptr';
+
+/**
+ * Class describing allocated property in parser's state
+ */
+export class Property {
+ constructor(public readonly ty: PropertyType, public readonly name: string) {
+ if (/^_/.test(name)) {
+ throw new Error(`Can't use internal property name: "${name}"`);
+ }
+ }
+}
diff --git a/llparse-builder/src/reachability.ts b/llparse-builder/src/reachability.ts
new file mode 100644
index 0000000..88bcd65
--- /dev/null
+++ b/llparse-builder/src/reachability.ts
@@ -0,0 +1,31 @@
+import { Node } from './node';
+
+/**
+ * This class finds all reachable nodes
+ */
+export class Reachability {
+ /**
+ * Build and return list of reachable nodes.
+ */
+ public build(root: Node): ReadonlyArray<Node> {
+ const res = new Set();
+ const queue = [ root ];
+ while (queue.length !== 0) {
+ const node = queue.pop()!;
+ if (res.has(node)) {
+ continue;
+ }
+ res.add(node);
+
+ for (const edge of node) {
+ queue.push(edge.node);
+ }
+
+ const otherwise = node.getOtherwiseEdge();
+ if (otherwise !== undefined) {
+ queue.push(otherwise.node);
+ }
+ }
+ return Array.from(res) as ReadonlyArray<Node>;
+ }
+}
diff --git a/llparse-builder/src/span-allocator.ts b/llparse-builder/src/span-allocator.ts
new file mode 100644
index 0000000..b3e8f6b
--- /dev/null
+++ b/llparse-builder/src/span-allocator.ts
@@ -0,0 +1,182 @@
+import * as assert from 'assert';
+import * as debugAPI from 'debug';
+
+import { Node, SpanEnd, SpanStart } from './node';
+import { Reachability } from './reachability';
+import { Span } from './span';
+
+const debug = debugAPI('llparse-builder:span-allocator');
+
+type SpanSet = Set<Span>;
+
+interface ISpanActiveInfo {
+ readonly active: Map<Node, SpanSet>;
+ readonly spans: ReadonlyArray<Span>;
+}
+
+type SpanOverlap = Map<Span, SpanSet>;
+
+export interface ISpanAllocatorResult {
+ readonly colors: ReadonlyMap<Span, number>;
+ readonly concurrency: ReadonlyArray<ReadonlyArray<Span> >;
+ readonly max: number;
+}
+
+function id(node: SpanStart | SpanEnd): Span {
+ return node.span;
+}
+
+export class SpanAllocator {
+ public allocate(root: Node): ISpanAllocatorResult {
+ const r = new Reachability();
+ const nodes = r.build(root);
+ const info = this.computeActive(nodes);
+ this.check(info);
+ const overlap = this.computeOverlap(info);
+ return this.color(info.spans, overlap);
+ }
+
+ private computeActive(nodes: ReadonlyArray<Node>): ISpanActiveInfo {
+ const activeMap: Map<Node, SpanSet> = new Map();
+ nodes.forEach((node) => activeMap.set(node, new Set()));
+
+ const queue: Set<Node> = new Set(nodes);
+ const spans: SpanSet = new Set();
+ for (const node of queue) {
+ queue.delete(node);
+
+ const active = activeMap.get(node)!;
+
+ if (node instanceof SpanStart) {
+ const span = id(node);
+ spans.add(span);
+ active.add(span);
+ }
+
+ active.forEach((span) => {
+ // Don't propagate span past the spanEnd
+ if (node instanceof SpanEnd && span === id(node)) {
+ return;
+ }
+
+ node.getAllEdges().forEach((edge) => {
+ const edgeNode = edge.node;
+
+ // Disallow loops
+ if (edgeNode instanceof SpanStart) {
+ assert.notStrictEqual(id(edgeNode), span,
+ `Detected loop in span "${span.callback.name}", started ` +
+ `at "${node.name}"`);
+ }
+
+ const edgeActive = activeMap.get(edgeNode)!;
+ if (edgeActive.has(span)) {
+ return;
+ }
+
+ edgeActive.add(span);
+ queue.add(edgeNode);
+ });
+ });
+ }
+
+ return { active: activeMap, spans: Array.from(spans) };
+ }
+
+ private check(info: ISpanActiveInfo): void {
+ debug('check start');
+ for (const [ node, spans ] of info.active) {
+ for (const edge of node.getAllEdges()) {
+ if (edge.node instanceof SpanStart) {
+ continue;
+ }
+
+ // Skip terminal nodes
+ if (edge.node.getAllEdges().length === 0) {
+ continue;
+ }
+
+ debug('checking edge from %j to %j', node.name, edge.node.name);
+
+ const edgeSpans = info.active.get(edge.node)!;
+ for (const subSpan of edgeSpans) {
+ assert(spans.has(subSpan),
+ `Unmatched span end for "${subSpan.callback.name}" ` +
+ `at "${edge.node.name}", coming from "${node.name}"`);
+ }
+
+ if (edge.node instanceof SpanEnd) {
+ const span = id(edge.node);
+ assert(spans.has(span),
+ `Unmatched span end for "${span.callback.name}"`);
+ }
+ }
+ }
+ }
+
+ private computeOverlap(info: ISpanActiveInfo): SpanOverlap {
+ const active = info.active;
+ const overlap: SpanOverlap = new Map();
+
+ info.spans.forEach((span) => overlap.set(span, new Set()));
+
+ active.forEach((spans) => {
+ spans.forEach((one) => {
+ const set = overlap.get(one)!;
+ spans.forEach((other) => {
+ if (other !== one) {
+ set.add(other);
+ }
+ });
+ });
+ });
+
+ return overlap;
+ }
+
+ private color(spans: ReadonlyArray<Span>, overlapMap: SpanOverlap)
+ : ISpanAllocatorResult {
+ let max = -1;
+ const colors: Map<Span, number> = new Map();
+
+ const allocate = (span: Span): number => {
+ if (colors.has(span)) {
+ return colors.get(span)!;
+ }
+
+ const overlap = overlapMap.get(span)!;
+
+ // See which colors are already used
+ const used: Set<number> = new Set();
+ for (const subSpan of overlap) {
+ if (colors.has(subSpan)) {
+ used.add(colors.get(subSpan)!);
+ }
+ }
+
+ // Find minimum available color
+ let i;
+ for (i = 0; used.has(i); i++) {
+ // no-op
+ }
+
+ max = Math.max(max, i);
+ colors.set(span, i);
+
+ return i;
+ };
+
+ const map: Map<Span, number> = new Map();
+
+ spans.forEach((span) => map.set(span, allocate(span)));
+
+ const concurrency: Span[][] = new Array(max + 1);
+ for (let i = 0; i < concurrency.length; i++) {
+ concurrency[i] = [];
+ }
+
+ spans.forEach((span) => concurrency[allocate(span)].push(span));
+
+ return { colors: map, concurrency, max };
+ }
+}
diff --git a/llparse-builder/src/span.ts b/llparse-builder/src/span.ts
new file mode 100644
index 0000000..99cafb0
--- /dev/null
+++ b/llparse-builder/src/span.ts
@@ -0,0 +1,57 @@
+import * as assert from 'assert';
+
+import { Span as SpanCallback } from './code';
+import { Node, SpanEnd, SpanStart } from './node';
+
+/**
+ * Spans are used for notifying parser user about matched data. Each byte after
+ * span start will be sent to the span callback until span end is called.
+ */
+export class Span {
+ private readonly startCache: Map<Node, SpanStart> = new Map();
+ private readonly endCache: Map<Node, SpanEnd> = new Map();
+
+ /**
+ * @param callback External callback, must be `code.span(...)` result.
+ */
+ constructor(public readonly callback: SpanCallback) {
+ }
+
+ /**
+ * Create `SpanStart` that indicates the start of the span.
+ *
+ * @param otherwise Optional convenience value. Same as calling
+ * `span.start().otherwise(...)`
+ */
+ public start(otherwise?: Node) {
+ if (otherwise !== undefined && this.startCache.has(otherwise)) {
+ return this.startCache.get(otherwise)!;
+ }
+
+ const res = new SpanStart(this);
+ if (otherwise !== undefined) {
+ res.otherwise(otherwise);
+ this.startCache.set(otherwise, res);
+ }
+ return res;
+ }
+
+ /**
+ * Create `SpanEnd` that indicates the end of the span.
+ *
+ * @param otherwise Optional convenience value. Same as calling
+ * `span.end().otherwise(...)`
+ */
+ public end(otherwise?: Node) {
+ if (otherwise !== undefined && this.endCache.has(otherwise)) {
+ return this.endCache.get(otherwise)!;
+ }
+
+ const res = new SpanEnd(this);
+ if (otherwise !== undefined) {
+ res.otherwise(otherwise);
+ this.endCache.set(otherwise, res);
+ }
+ return res;
+ }
+}
diff --git a/llparse-builder/src/transform/base.ts b/llparse-builder/src/transform/base.ts
new file mode 100644
index 0000000..902199c
--- /dev/null
+++ b/llparse-builder/src/transform/base.ts
@@ -0,0 +1,12 @@
+export type TransformName = 'to_lower_unsafe' | 'to_lower';
+
+/**
+ * Character transformation.
+ */
+export abstract class Transform {
+ /**
+ * @param name Transform name
+ */
+ constructor(public readonly name: TransformName) {
+ }
+}
diff --git a/llparse-builder/src/transform/creator.ts b/llparse-builder/src/transform/creator.ts
new file mode 100644
index 0000000..eaf3d5c
--- /dev/null
+++ b/llparse-builder/src/transform/creator.ts
@@ -0,0 +1,28 @@
+import { Transform } from './base';
+import { ToLower } from './to-lower';
+import { ToLowerUnsafe } from './to-lower-unsafe';
+
+/**
+ * API for creating character transformations.
+ *
+ * The results of methods of this class can be used as an argument to:
+ * `p.node().transform(...)`.
+ */
+export class Creator {
+ /**
+ * Unsafe transform to lowercase.
+ *
+ * The operation of this transformation is equivalent to:
+ * `String.fromCharCode(input.charCodeAt(0) | 0x20)`.
+ */
+ public toLowerUnsafe(): Transform {
+ return new ToLowerUnsafe();
+ }
+
+ /**
+ * Safe transform to lowercase.
+ */
+ public toLower(): Transform {
+ return new ToLower();
+ }
+}
diff --git a/llparse-builder/src/transform/index.ts b/llparse-builder/src/transform/index.ts
new file mode 100644
index 0000000..acdcf01
--- /dev/null
+++ b/llparse-builder/src/transform/index.ts
@@ -0,0 +1,3 @@
+export { Transform } from './base';
+export { Creator } from './creator';
+export { ToLowerUnsafe } from './to-lower-unsafe';
diff --git a/llparse-builder/src/transform/to-lower-unsafe.ts b/llparse-builder/src/transform/to-lower-unsafe.ts
new file mode 100644
index 0000000..99d9618
--- /dev/null
+++ b/llparse-builder/src/transform/to-lower-unsafe.ts
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform {
+ constructor() {
+ super('to_lower_unsafe');
+ }
+}
diff --git a/llparse-builder/src/transform/to-lower.ts b/llparse-builder/src/transform/to-lower.ts
new file mode 100644
index 0000000..b333fce
--- /dev/null
+++ b/llparse-builder/src/transform/to-lower.ts
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ToLower extends Transform {
+ constructor() {
+ super('to_lower');
+ }
+}
diff --git a/llparse-builder/src/utils.ts b/llparse-builder/src/utils.ts
new file mode 100644
index 0000000..3521b20
--- /dev/null
+++ b/llparse-builder/src/utils.ts
@@ -0,0 +1,19 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+
+/**
+ * Internal
+ */
+export function toBuffer(value: number | string | Buffer): Buffer {
+ let res: Buffer;
+ if (Buffer.isBuffer(value)) {
+ res = value;
+ } else if (typeof value === 'string') {
+ res = Buffer.from(value);
+ } else {
+ assert(0 <= value && value <= 0xff, 'Invalid byte value');
+ res = Buffer.from([ value ]);
+ }
+ assert(res.length >= 1, 'Invalid key length');
+ return res;
+}
diff --git a/llparse-builder/test/builder-test.ts b/llparse-builder/test/builder-test.ts
new file mode 100644
index 0000000..82723ec
--- /dev/null
+++ b/llparse-builder/test/builder-test.ts
@@ -0,0 +1,94 @@
+import * as assert from 'assert';
+
+import { Builder } from '../src/builder';
+
+describe('LLParse/Builder', () => {
+ let b: Builder;
+ beforeEach(() => {
+ b = new Builder();
+ });
+
+ it('should build primitive graph', () => {
+ const start = b.node('start');
+ const end = b.node('end');
+
+ start
+ .peek('e', end)
+ .match('a', start)
+ .otherwise(b.error(1, 'error'));
+
+ end
+ .skipTo(start);
+
+ const edges = start.getEdges();
+ assert.strictEqual(edges.length, 2);
+
+ assert(!edges[0].noAdvance);
+ assert.strictEqual(edges[0].node, start);
+
+ assert(edges[1].noAdvance);
+ assert.strictEqual(edges[1].node, end);
+ });
+
+ it('should disallow duplicate edges', () => {
+ const start = b.node('start');
+
+ start.peek('e', start);
+
+ assert.throws(() => {
+ start.peek('e', start);
+ }, /duplicate edge/);
+ });
+
+ it('should disallow select to non-invoke', () => {
+ const start = b.node('start');
+
+ assert.throws(() => {
+ start.select('a', 1, start);
+ }, /value to non-Invoke/);
+ });
+
+ it('should disallow select to match-invoke', () => {
+ const start = b.node('start');
+ const invoke = b.invoke(b.code.match('something'));
+
+ assert.throws(() => {
+ start.select('a', 1, invoke);
+ }, /Invalid.*code signature/);
+ });
+
+ it('should disallow peek to value-invoke', () => {
+ const start = b.node('start');
+ const invoke = b.invoke(b.code.value('something'));
+
+ assert.throws(() => {
+ start.peek('a', invoke);
+ }, /Invalid.*code signature/);
+ });
+
+ it('should allow select to value-invoke', () => {
+ const start = b.node('start');
+ const invoke = b.invoke(b.code.value('something'));
+
+ assert.doesNotThrow(() => {
+ start.select('a', 1, invoke);
+ });
+ });
+
+ it('should create edges for Invoke', () => {
+ const start = b.node('start');
+ const invoke = b.invoke(b.code.value('something'), {
+ '-1': start,
+ '1': start,
+ '10': start,
+ });
+
+ const edges = invoke.getEdges();
+ const keys = edges.map((edge) => edge.key!);
+ assert.deepStrictEqual(keys, [
+ -1,
+ 1,
+ 10,
+ ]);
+ });
+});
diff --git a/llparse-builder/test/loop-checker-test.ts b/llparse-builder/test/loop-checker-test.ts
new file mode 100644
index 0000000..0df6064
--- /dev/null
+++ b/llparse-builder/test/loop-checker-test.ts
@@ -0,0 +1,118 @@
+import * as assert from 'assert';
+
+import { Builder, LoopChecker } from '../src/builder';
+
+describe('LLParse/LoopChecker', () => {
+ let b: Builder;
+ let lc: LoopChecker;
+ beforeEach(() => {
+ b = new Builder();
+ lc = new LoopChecker();
+ });
+
+ it('should detect shallow loops', () => {
+ const start = b.node('start');
+
+ start
+ .otherwise(start);
+
+ assert.throws(() => {
+ lc.check(start);
+ }, /Detected loop in "start".*"start"/);
+ });
+
+ it('should detect loops', () => {
+ const start = b.node('start');
+ const a = b.node('a');
+ const invoke = b.invoke(b.code.match('nop'), {
+ 0: start,
+ }, b.error(1, 'error'));
+
+ start
+ .peek('a', a)
+ .otherwise(b.error(1, 'error'));
+
+ a.otherwise(invoke);
+
+ assert.throws(() => {
+ lc.check(start);
+ }, /Detected loop in "a".*"a" -> "invoke_nop"/);
+ });
+
+ it('should detect seemingly unreachable keys', () => {
+ const start = b.node('start');
+ const loop = b.node('loop');
+
+ start
+ .peek('a', loop)
+ .otherwise(b.error(1, 'error'));
+
+ loop
+ .match('a', loop)
+ .otherwise(loop);
+
+ assert.throws(() => {
+ lc.check(start);
+ }, /Detected loop in "loop" through.*"loop"/);
+ });
+
+ it('should ignore loops through `peek` to `match`', () => {
+ const start = b.node('start');
+ const a = b.node('a');
+ const invoke = b.invoke(b.code.match('nop'), {
+ 0: start,
+ }, b.error(1, 'error'));
+
+ start
+ .peek('a', a)
+ .otherwise(b.error(1, 'error'));
+
+ a
+ .match('abc', invoke)
+ .otherwise(start);
+
+ assert.doesNotThrow(() => lc.check(start));
+ });
+
+ it('should ignore irrelevant `peek`s', () => {
+ const start = b.node('start');
+ const a = b.node('a');
+
+ start
+ .peek('a', a)
+ .otherwise(b.error(1, 'error'));
+
+ a
+ .peek('b', start)
+ .otherwise(b.error(1, 'error'));
+
+ assert.doesNotThrow(() => lc.check(start));
+ });
+
+ it('should ignore loops with multi `peek`/`match`', () => {
+ const start = b.node('start');
+ const another = b.node('another');
+
+ const NUM: ReadonlyArray<string> = [
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ ];
+
+ const ALPHA: ReadonlyArray<string> = [
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ ];
+
+ start
+ .match(ALPHA, start)
+ .peek(NUM, another)
+ .skipTo(start);
+
+ another
+ .match(NUM, another)
+ .otherwise(start);
+
+ assert.doesNotThrow(() => lc.check(start));
+ });
+});
diff --git a/llparse-builder/test/span-allocator-test.ts b/llparse-builder/test/span-allocator-test.ts
new file mode 100644
index 0000000..bc8f656
--- /dev/null
+++ b/llparse-builder/test/span-allocator-test.ts
@@ -0,0 +1,146 @@
+import * as assert from 'assert';
+
+import { Builder, SpanAllocator } from '../src/builder';
+
+describe('LLParse/LoopChecker', () => {
+ let b: Builder;
+ let sa: SpanAllocator;
+ beforeEach(() => {
+ b = new Builder();
+ sa = new SpanAllocator();
+ });
+
+ it('should allocate single span', () => {
+ const span = b.span(b.code.span('span'));
+ const start = b.node('start');
+ const body = b.node('body');
+
+ start
+ .otherwise(span.start(body));
+
+ body
+ .skipTo(span.end(start));
+
+ const res = sa.allocate(start);
+
+ assert.strictEqual(res.max, 0);
+
+ assert.strictEqual(res.concurrency.length, 1);
+ assert.ok(res.concurrency[0].includes(span));
+
+ assert.strictEqual(res.colors.size, 1);
+ assert.strictEqual(res.colors.get(span), 0);
+ });
+
+ it('should allocate overlapping spans', () => {
+ const span1 = b.span(b.code.span('span1'));
+ const span2 = b.span(b.code.span('span2'));
+
+ const start = b.node('start');
+ const body1 = b.node('body1');
+ const body2 = b.node('body2');
+
+ start
+ .otherwise(span1.start(body1));
+
+ body1
+ .otherwise(span2.start(body2));
+
+ body2
+ .skipTo(span2.end(span1.end(start)));
+
+ const res = sa.allocate(start);
+
+ assert.strictEqual(res.max, 1);
+
+ assert.strictEqual(res.concurrency.length, 2);
+ assert.ok(res.concurrency[0].includes(span1));
+ assert.ok(res.concurrency[1].includes(span2));
+
+ assert.strictEqual(res.colors.size, 2);
+ assert.strictEqual(res.colors.get(span1), 0);
+ assert.strictEqual(res.colors.get(span2), 1);
+ });
+
+ it('should allocate non-overlapping spans', () => {
+ const span1 = b.span(b.code.span('span1'));
+ const span2 = b.span(b.code.span('span2'));
+
+ const start = b.node('start');
+ const body1 = b.node('body1');
+ const body2 = b.node('body2');
+
+ start
+ .match('a', span1.start(body1))
+ .otherwise(span2.start(body2));
+
+ body1
+ .skipTo(span1.end(start));
+
+ body2
+ .skipTo(span2.end(start));
+
+ const res = sa.allocate(start);
+
+ assert.strictEqual(res.max, 0);
+
+ assert.strictEqual(res.concurrency.length, 1);
+ assert.ok(res.concurrency[0].includes(span1));
+ assert.ok(res.concurrency[0].includes(span2));
+
+ assert.strictEqual(res.colors.size, 2);
+ assert.strictEqual(res.colors.get(span1), 0);
+ assert.strictEqual(res.colors.get(span2), 0);
+ });
+
+ it('should throw on loops', () => {
+ const span = b.span(b.code.span('span_name'));
+
+ const start = b.node('start');
+
+ start
+ .otherwise(span.start(start));
+
+ assert.throws(() => {
+ sa.allocate(start);
+ }, /loop.*span_name/);
+ });
+
+ it('should throw on unmatched ends', () => {
+ const start = b.node('start');
+ const span = b.span(b.code.span('on_data'));
+
+ start.otherwise(span.end().skipTo(start));
+
+ assert.throws(() => sa.allocate(start), /unmatched.*on_data/i);
+ });
+
+ it('should throw on branched unmatched ends', () => {
+ const start = b.node('start');
+ const end = b.node('end');
+ const span = b.span(b.code.span('on_data'));
+
+ start
+ .match('a', end)
+ .match('b', span.start(end))
+ .otherwise(b.error(1, 'error'));
+
+ end
+ .otherwise(span.end(start));
+
+ assert.throws(() => sa.allocate(start), /unmatched.*on_data/i);
+ });
+
+ it('should propagate through the Invoke map', () => {
+ const start = b.node('start');
+ const span = b.span(b.code.span('llparse__on_data'));
+
+ b.property('i8', 'custom');
+
+ start.otherwise(b.invoke(b.code.load('custom'), {
+ 0: span.end().skipTo(start),
+ }, span.end().skipTo(start)));
+
+ assert.doesNotThrow(() => sa.allocate(span.start(start)));
+ });
+});
diff --git a/llparse-builder/tsconfig.json b/llparse-builder/tsconfig.json
new file mode 100644
index 0000000..01ec7c2
--- /dev/null
+++ b/llparse-builder/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "target": "es2017",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./lib",
+ "declaration": true,
+ "pretty": true,
+ "sourceMap": true
+ },
+ "include": [
+ "src/**/*.ts"
+ ]
+}
diff --git a/llparse-builder/tslint.json b/llparse-builder/tslint.json
new file mode 100644
index 0000000..b0aaf97
--- /dev/null
+++ b/llparse-builder/tslint.json
@@ -0,0 +1,14 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "no-bitwise": null,
+ "quotemark": [
+ true, "single", "avoid-escape", "avoid-template"
+ ]
+ },
+ "rulesDirectory": []
+}
diff --git a/llparse-frontend/.gitignore b/llparse-frontend/.gitignore
new file mode 100644
index 0000000..88edb62
--- /dev/null
+++ b/llparse-frontend/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+lib/
diff --git a/llparse-frontend/.travis.yml b/llparse-frontend/.travis.yml
new file mode 100644
index 0000000..03f4af5
--- /dev/null
+++ b/llparse-frontend/.travis.yml
@@ -0,0 +1,6 @@
+sudo: false
+language: node_js
+node_js:
+ - "stable"
+script:
+ npm test
diff --git a/llparse-frontend/README.md b/llparse-frontend/README.md
new file mode 100644
index 0000000..359dd9b
--- /dev/null
+++ b/llparse-frontend/README.md
@@ -0,0 +1,30 @@
+# llparse-frontend
+[![Build Status](https://secure.travis-ci.org/indutny/llparse-frontend.svg)](http://travis-ci.org/indutny/llparse-frontend)
+[![NPM version](https://badge.fury.io/js/llparse-frontend.svg)](https://badge.fury.io/js/llparse-frontend)
+
+WIP
+
+#### LICENSE
+
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/llparse-frontend/package-lock.json b/llparse-frontend/package-lock.json
new file mode 100644
index 0000000..3cfef7a
--- /dev/null
+++ b/llparse-frontend/package-lock.json
@@ -0,0 +1,1516 @@
+{
+ "name": "llparse-frontend",
+ "version": "3.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.3",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz",
+ "integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.3"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
+ "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.3",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz",
+ "integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.3",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
+ "dev": true
+ },
+ "@types/mocha": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
+ "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "14.11.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz",
+ "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==",
+ "dev": true
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array.prototype.map": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
+ "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.4"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+ "dev": true
+ },
+ "binary-search": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
+ "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chokidar": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+ "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.4.0"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true
+ },
+ "es-get-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
+ "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
+ "dev": true,
+ "requires": {
+ "es-abstract": "^1.17.4",
+ "has-symbols": "^1.0.1",
+ "is-arguments": "^1.0.4",
+ "is-map": "^2.0.1",
+ "is-set": "^2.0.1",
+ "is-string": "^1.0.5",
+ "isarray": "^2.0.5"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
+ "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
+ "dev": true
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-set": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
+ "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
+ "dev": true
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "iterate-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
+ "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
+ "dev": true
+ },
+ "iterate-value": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
+ "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
+ "dev": true,
+ "requires": {
+ "es-get-iterator": "^1.0.2",
+ "iterate-iterator": "^1.0.1"
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "llparse-builder": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/llparse-builder/-/llparse-builder-1.5.2.tgz",
+ "integrity": "sha512-i862UNC3YUEdlfK/NUCJxlKjtWjgAI9AJXDRgjcfRHfwFt4Sf8eFPTRsc91/2R9MBZ0kyFdfhi8SVhMsZf1gNQ==",
+ "requires": {
+ "@types/debug": "4.1.5 ",
+ "binary-search": "^1.3.6",
+ "debug": "^4.2.0"
+ },
+ "dependencies": {
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
+ },
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "log-symbols": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+ "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ }
+ }
+ },
+ "mocha": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz",
+ "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.4.2",
+ "debug": "4.1.1",
+ "diff": "4.0.2",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.1.6",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.14.0",
+ "log-symbols": "4.0.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.2",
+ "object.assign": "4.1.0",
+ "promise.allsettled": "1.0.2",
+ "serialize-javascript": "4.0.0",
+ "strip-json-comments": "3.0.1",
+ "supports-color": "7.1.0",
+ "which": "2.0.2",
+ "wide-align": "1.1.3",
+ "workerpool": "6.0.0",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "p-limit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "promise.allsettled": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
+ "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
+ "dev": true,
+ "requires": {
+ "array.prototype.map": "^1.0.1",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "iterate-value": "^1.0.0"
+ }
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "ts-node": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+ "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.17",
+ "yn": "3.1.1"
+ }
+ },
+ "tslib": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+ "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+ "dev": true
+ },
+ "tslint": {
+ "version": "5.20.1",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
+ "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.8.0",
+ "tsutils": "^2.29.0"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ }
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "typescript": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
+ "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "workerpool": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
+ "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
+ "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "decamelize": "^1.2.0",
+ "flat": "^4.1.0",
+ "is-plain-obj": "^1.1.0",
+ "yargs": "^14.2.3"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "yargs": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
+ "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^15.0.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
+ "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/llparse-frontend/package.json b/llparse-frontend/package.json
new file mode 100644
index 0000000..8afea88
--- /dev/null
+++ b/llparse-frontend/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "llparse-frontend",
+ "version": "3.0.0",
+ "description": "Frontend for LLParse compiler",
+ "main": "lib/frontend.js",
+ "types": "lib/frontend.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "rm -rf lib",
+ "prepare": "npm run clean && npm run build",
+ "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
+ "fix-lint": "npm run lint -- --fix",
+ "mocha": "mocha --timeout=10000 -r ts-node/register/type-check --reporter spec test/*-test.ts",
+ "test": "npm run mocha && npm run lint"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/indutny/llparse-frontend.git"
+ },
+ "keywords": [
+ "llparse",
+ "frontend"
+ ],
+ "author": "Fedor Indutny <fedor@indutny.com> (http://darksi.de/)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/indutny/llparse-frontend/issues"
+ },
+ "homepage": "https://github.com/indutny/llparse-frontend#readme",
+ "dependencies": {
+ "debug": "^3.2.6",
+ "llparse-builder": "^1.5.2"
+ },
+ "devDependencies": {
+ "@types/debug": "^4.1.5",
+ "@types/mocha": "^8.0.3",
+ "@types/node": "^14.11.8",
+ "mocha": "^8.1.3",
+ "ts-node": "^9.0.0",
+ "tslint": "^5.20.1",
+ "typescript": "^4.0.3"
+ }
+}
diff --git a/llparse-frontend/src/code/and.ts b/llparse-frontend/src/code/and.ts
new file mode 100644
index 0000000..54dc5fd
--- /dev/null
+++ b/llparse-frontend/src/code/and.ts
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class And extends FieldValue {
+ constructor(name: string, field: string, value: number) {
+ super('match', `and_${field}_${toCacheKey(value)}`, name, field, value);
+ }
+}
diff --git a/llparse-frontend/src/code/base.ts b/llparse-frontend/src/code/base.ts
new file mode 100644
index 0000000..cde4b6d
--- /dev/null
+++ b/llparse-frontend/src/code/base.ts
@@ -0,0 +1,8 @@
+export type Signature = 'match' | 'value' | 'span';
+
+export abstract class Code {
+ constructor(public readonly signature: Signature,
+ public readonly cacheKey: string,
+ public readonly name: string) {
+ }
+}
diff --git a/llparse-frontend/src/code/external.ts b/llparse-frontend/src/code/external.ts
new file mode 100644
index 0000000..f4254c1
--- /dev/null
+++ b/llparse-frontend/src/code/external.ts
@@ -0,0 +1,7 @@
+import { Code, Signature } from './base';
+
+export abstract class External extends Code {
+ constructor(signature: Signature, name: string) {
+ super(signature, 'external_' + name, name);
+ }
+}
diff --git a/llparse-frontend/src/code/field-value.ts b/llparse-frontend/src/code/field-value.ts
new file mode 100644
index 0000000..1c7c109
--- /dev/null
+++ b/llparse-frontend/src/code/field-value.ts
@@ -0,0 +1,13 @@
+import * as assert from 'assert';
+
+import { Signature } from './base';
+import { Field } from './field';
+
+export abstract class FieldValue extends Field {
+ constructor(signature: Signature, cacheKey: string, name: string,
+ field: string, public readonly value: number) {
+ super(signature, cacheKey, name, field);
+
+ assert.strictEqual(value, value | 0, 'FieldValue `value` must be integer');
+ }
+}
diff --git a/llparse-frontend/src/code/field.ts b/llparse-frontend/src/code/field.ts
new file mode 100644
index 0000000..c60b8ef
--- /dev/null
+++ b/llparse-frontend/src/code/field.ts
@@ -0,0 +1,8 @@
+import { Code, Signature } from './base';
+
+export abstract class Field extends Code {
+ constructor(signature: Signature, cacheKey: string, name: string,
+ public readonly field: string) {
+ super(signature, cacheKey, name);
+ }
+}
diff --git a/llparse-frontend/src/code/index.ts b/llparse-frontend/src/code/index.ts
new file mode 100644
index 0000000..c7d5c69
--- /dev/null
+++ b/llparse-frontend/src/code/index.ts
@@ -0,0 +1,15 @@
+export * from './and';
+export * from './base';
+export * from './external';
+export * from './field-value';
+export * from './field';
+export * from './is-equal';
+export * from './load';
+export * from './match';
+export * from './mul-add';
+export * from './or';
+export * from './span';
+export * from './store';
+export * from './test';
+export * from './update';
+export * from './value';
diff --git a/llparse-frontend/src/code/is-equal.ts b/llparse-frontend/src/code/is-equal.ts
new file mode 100644
index 0000000..16a2ee2
--- /dev/null
+++ b/llparse-frontend/src/code/is-equal.ts
@@ -0,0 +1,9 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class IsEqual extends FieldValue {
+ constructor(name: string, field: string, value: number) {
+ super('match', `is_equal_${field}_${toCacheKey(value)}`, name, field,
+ value);
+ }
+}
diff --git a/llparse-frontend/src/code/load.ts b/llparse-frontend/src/code/load.ts
new file mode 100644
index 0000000..76b715a
--- /dev/null
+++ b/llparse-frontend/src/code/load.ts
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Load extends Field {
+ constructor(name: string, field: string) {
+ super('match', `load_${field}`, name, field);
+ }
+}
diff --git a/llparse-frontend/src/code/match.ts b/llparse-frontend/src/code/match.ts
new file mode 100644
index 0000000..819d2af
--- /dev/null
+++ b/llparse-frontend/src/code/match.ts
@@ -0,0 +1,7 @@
+import { External } from './external';
+
+export class Match extends External {
+ constructor(name: string) {
+ super('match', name);
+ }
+}
diff --git a/llparse-frontend/src/code/mul-add.ts b/llparse-frontend/src/code/mul-add.ts
new file mode 100644
index 0000000..c99be0d
--- /dev/null
+++ b/llparse-frontend/src/code/mul-add.ts
@@ -0,0 +1,26 @@
+import { toCacheKey } from '../utils';
+import { Field } from './field';
+
+export interface IMulAddOptions {
+ readonly base: number;
+ readonly max?: number;
+ readonly signed: boolean;
+}
+
+function toOptionsKey(options: IMulAddOptions): string {
+ let res = `base_${toCacheKey(options.base)}`;
+ if (options.max !== undefined) {
+ res += `_max_${toCacheKey(options.max)}`;
+ }
+ if (options.signed !== undefined) {
+ res += `_signed_${toCacheKey(options.signed)}`;
+ }
+ return res;
+}
+
+export class MulAdd extends Field {
+ constructor(name: string, field: string,
+ public readonly options: IMulAddOptions) {
+ super('value', `mul_add_${field}_${toOptionsKey(options)}`, name, field);
+ }
+}
diff --git a/llparse-frontend/src/code/or.ts b/llparse-frontend/src/code/or.ts
new file mode 100644
index 0000000..2328a9f
--- /dev/null
+++ b/llparse-frontend/src/code/or.ts
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class Or extends FieldValue {
+ constructor(name: string, field: string, value: number) {
+ super('match', `or_${field}_${toCacheKey(value)}`, name, field, value);
+ }
+}
diff --git a/llparse-frontend/src/code/span.ts b/llparse-frontend/src/code/span.ts
new file mode 100644
index 0000000..6241e03
--- /dev/null
+++ b/llparse-frontend/src/code/span.ts
@@ -0,0 +1,7 @@
+import { External } from './external';
+
+export class Span extends External {
+ constructor(name: string) {
+ super('span', name);
+ }
+}
diff --git a/llparse-frontend/src/code/store.ts b/llparse-frontend/src/code/store.ts
new file mode 100644
index 0000000..c2cb9ea
--- /dev/null
+++ b/llparse-frontend/src/code/store.ts
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Store extends Field {
+ constructor(name: string, field: string) {
+ super('value', `store_${field}`, name, field);
+ }
+}
diff --git a/llparse-frontend/src/code/test.ts b/llparse-frontend/src/code/test.ts
new file mode 100644
index 0000000..21339e9
--- /dev/null
+++ b/llparse-frontend/src/code/test.ts
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class Test extends FieldValue {
+ constructor(name: string, field: string, value: number) {
+ super('match', `test_${field}_${toCacheKey(value)}`, name, field, value);
+ }
+}
diff --git a/llparse-frontend/src/code/update.ts b/llparse-frontend/src/code/update.ts
new file mode 100644
index 0000000..5fa5eec
--- /dev/null
+++ b/llparse-frontend/src/code/update.ts
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class Update extends FieldValue {
+ constructor(name: string, field: string, value: number) {
+ super('match', `update_${field}_${toCacheKey(value)}`, name, field, value);
+ }
+}
diff --git a/llparse-frontend/src/code/value.ts b/llparse-frontend/src/code/value.ts
new file mode 100644
index 0000000..4f32ae8
--- /dev/null
+++ b/llparse-frontend/src/code/value.ts
@@ -0,0 +1,7 @@
+import { External } from './external';
+
+export class Value extends External {
+ constructor(name: string) {
+ super('value', name);
+ }
+}
diff --git a/llparse-frontend/src/container/index.ts b/llparse-frontend/src/container/index.ts
new file mode 100644
index 0000000..a62aac8
--- /dev/null
+++ b/llparse-frontend/src/container/index.ts
@@ -0,0 +1,84 @@
+import * as assert from 'assert';
+
+import { ICodeImplementation } from '../implementation/code';
+import { IImplementation } from '../implementation/full';
+import { INodeImplementation } from '../implementation/node';
+import { ITransformImplementation } from '../implementation/transform';
+import { IWrap } from '../wrap';
+import { ContainerWrap } from './wrap';
+
+export { ContainerWrap };
+
+export class Container {
+ private readonly map: Map<string, IImplementation> = new Map();
+
+ public add(key: string, impl: IImplementation): void {
+ assert(!this.map.has(key), `Duplicate implementation key: "${key}"`);
+ this.map.set(key, impl);
+ }
+
+ public build(): IImplementation {
+ return {
+ code: this.buildCode(),
+ node: this.buildNode(),
+ transform: this.buildTransform(),
+ };
+ }
+
+ public buildCode(): ICodeImplementation {
+ return {
+ And: this.combine((impl) => impl.code.And),
+ IsEqual: this.combine((impl) => impl.code.IsEqual),
+ Load: this.combine((impl) => impl.code.Load),
+ Match: this.combine((impl) => impl.code.Match),
+ MulAdd: this.combine((impl) => impl.code.MulAdd),
+ Or: this.combine((impl) => impl.code.Or),
+ Span: this.combine((impl) => impl.code.Span),
+ Store: this.combine((impl) => impl.code.Store),
+ Test: this.combine((impl) => impl.code.Test),
+ Update: this.combine((impl) => impl.code.Update),
+ Value: this.combine((impl) => impl.code.Value),
+ };
+ }
+
+ public buildNode(): INodeImplementation {
+ return {
+ Consume: this.combine((impl) => impl.node.Consume),
+ Empty: this.combine((impl) => impl.node.Empty),
+ Error: this.combine((impl) => impl.node.Error),
+ Invoke: this.combine((impl) => impl.node.Invoke),
+ Pause: this.combine((impl) => impl.node.Pause),
+ Sequence: this.combine((impl) => impl.node.Sequence),
+ Single: this.combine((impl) => impl.node.Single),
+ SpanEnd: this.combine((impl) => impl.node.SpanEnd),
+ SpanStart: this.combine((impl) => impl.node.SpanStart),
+ TableLookup: this.combine((impl) => impl.node.TableLookup),
+ };
+ }
+
+ public buildTransform(): ITransformImplementation {
+ return {
+ ID: this.combine((impl) => impl.transform.ID),
+ ToLower: this.combine((impl) => impl.transform.ToLower),
+ ToLowerUnsafe: this.combine((impl) => impl.transform.ToLowerUnsafe),
+ };
+ }
+
+ private combine<T>(gather: (impl: IImplementation) => new(n: T) => IWrap<T>)
+ : new(n: T) => ContainerWrap<T> {
+ const wraps: Map<string, new(n: T) => IWrap<T>> = new Map();
+ for (const [ key, impl ] of this.map) {
+ wraps.set(key, gather(impl));
+ }
+
+ return class ContainerWrapSingle extends ContainerWrap<T> {
+ constructor(ref: T) {
+ super(ref);
+
+ for (const [ key, impl ] of wraps) {
+ this.map.set(key, new impl(ref));
+ }
+ }
+ };
+ }
+}
diff --git a/llparse-frontend/src/container/wrap.ts b/llparse-frontend/src/container/wrap.ts
new file mode 100644
index 0000000..f3b886c
--- /dev/null
+++ b/llparse-frontend/src/container/wrap.ts
@@ -0,0 +1,15 @@
+import * as assert from 'assert';
+
+import { IWrap } from '../wrap';
+
+export class ContainerWrap<T> {
+ protected readonly map: Map<string, IWrap<T>> = new Map();
+
+ constructor(public readonly ref: T) {
+ }
+
+ public get<R extends IWrap<T>>(key: string): R {
+ assert(this.map.has(key), `Unknown implementation key "${key}"`);
+ return this.map.get(key)! as R;
+ }
+}
diff --git a/llparse-frontend/src/enumerator.ts b/llparse-frontend/src/enumerator.ts
new file mode 100644
index 0000000..f2940a2
--- /dev/null
+++ b/llparse-frontend/src/enumerator.ts
@@ -0,0 +1,23 @@
+import { Node } from './node';
+import { IWrap } from './wrap';
+
+export class Enumerator {
+ public getAllNodes(root: IWrap<Node>): ReadonlyArray<IWrap<Node>> {
+ const nodes: Set<IWrap<Node>> = new Set();
+ const queue = [ root ];
+
+ while (queue.length !== 0) {
+ const node = queue.pop()!;
+ for (const slot of node.ref.getSlots()) {
+ if (nodes.has(slot.node)) {
+ continue;
+ }
+
+ nodes.add(slot.node);
+ queue.push(slot.node);
+ }
+ }
+
+ return Array.from(nodes);
+ }
+}
diff --git a/llparse-frontend/src/frontend.ts b/llparse-frontend/src/frontend.ts
new file mode 100644
index 0000000..91c5224
--- /dev/null
+++ b/llparse-frontend/src/frontend.ts
@@ -0,0 +1,513 @@
+import * as assert from 'assert';
+import * as debugAPI from 'debug';
+import * as source from 'llparse-builder';
+
+import * as frontend from './namespace/frontend';
+import { Container, ContainerWrap } from './container';
+import { IImplementation } from './implementation';
+import { SpanField } from './span-field';
+import { Trie, TrieEmpty, TrieNode, TrieSequence, TrieSingle } from './trie';
+import { Identifier, IUniqueName } from './utils';
+import { IWrap } from './wrap';
+import { Enumerator } from './enumerator';
+import { Peephole } from './peephole';
+
+const debug = debugAPI('llparse:translator');
+
+export { code, node, transform } from './namespace/frontend';
+
+export {
+ source,
+ Identifier,
+ IUniqueName,
+ IWrap,
+ SpanField,
+ Container,
+ ContainerWrap,
+};
+
+// Minimum number of cases of `single` node to make it eligable for
+// `TableLookup` optimization
+export const DEFAULT_MIN_TABLE_SIZE = 32;
+
+// Maximum width of entry in a table for a `TableLookup` optimization
+export const DEFAULT_MAX_TABLE_WIDTH = 4;
+
+type WrappedNode = IWrap<frontend.node.Node>;
+type WrappedCode = IWrap<frontend.code.Code>;
+
+export interface IFrontendLazyOptions {
+ readonly maxTableElemWidth?: number;
+ readonly minTableSize?: number;
+}
+
+export interface IFrontendResult {
+ readonly prefix: string;
+ readonly properties: ReadonlyArray<source.Property>;
+ readonly root: IWrap<frontend.node.Node>;
+ readonly spans: ReadonlyArray<SpanField>;
+ readonly resumptionTargets: ReadonlySet<WrappedNode>;
+}
+
+interface IFrontendOptions {
+ readonly maxTableElemWidth: number;
+ readonly minTableSize: number;
+}
+
+type MatchChildren = WrappedNode[];
+type MatchResult = WrappedNode | ReadonlyArray<WrappedNode>;
+
+interface ITableLookupTarget {
+ readonly keys: number[];
+ readonly noAdvance: boolean;
+ readonly trie: TrieEmpty;
+}
+
+export class Frontend {
+ private readonly options: IFrontendOptions;
+
+ private readonly id: Identifier = new Identifier(this.prefix + '__n_');
+ private readonly codeId: Identifier = new Identifier(this.prefix + '__c_');
+ private readonly map: Map<source.node.Node, WrappedNode> = new Map();
+ private readonly spanMap: Map<source.Span, SpanField> = new Map();
+ private readonly codeCache: Map<string, WrappedCode> = new Map();
+ private readonly resumptionTargets: Set<WrappedNode> = new Set();
+
+ constructor(private readonly prefix: string,
+ private readonly implementation: IImplementation,
+ options: IFrontendLazyOptions = {}) {
+ this.options = {
+ maxTableElemWidth: options.maxTableElemWidth === undefined ?
+ DEFAULT_MAX_TABLE_WIDTH : options.maxTableElemWidth,
+ minTableSize: options.minTableSize === undefined ?
+ DEFAULT_MIN_TABLE_SIZE : options.minTableSize,
+ };
+
+ assert(0 < this.options.maxTableElemWidth,
+ 'Invalid `options.maxTableElemWidth`, must be positive');
+ }
+
+ public compile(root: source.node.Node,
+ properties: ReadonlyArray<source.Property>): IFrontendResult {
+ debug('checking loops');
+ const lc = new source.LoopChecker();
+ lc.check(root);
+
+ debug('allocating spans');
+ const spanAllocator = new source.SpanAllocator();
+ const sourceSpans = spanAllocator.allocate(root);
+
+ const spans = sourceSpans.concurrency.map((concurrent, index) => {
+ const span = new SpanField(index, concurrent.map((sourceSpan) => {
+ return this.translateSpanCode(sourceSpan.callback);
+ }));
+
+ for (const sourceSpan of concurrent) {
+ this.spanMap.set(sourceSpan, span);
+ }
+
+ return span;
+ });
+
+ debug('translating');
+ let out = this.translate(root);
+
+ debug('enumerating');
+ const enumerator = new Enumerator();
+ let nodes = enumerator.getAllNodes(out);
+
+ debug('peephole optimization');
+ const peephole = new Peephole();
+ out = peephole.optimize(out, nodes);
+
+ debug('re-enumerating');
+ nodes = enumerator.getAllNodes(out);
+
+ debug('registering resumption targets');
+ this.resumptionTargets.add(out);
+ for (const node of nodes) {
+ this.registerNode(node);
+ }
+
+ return {
+ prefix: this.prefix,
+ properties,
+ resumptionTargets: this.resumptionTargets,
+ root: out,
+ spans,
+ };
+ }
+
+ // TODO(indutny): remove this in the next major release
+ public getResumptionTargets(): ReadonlySet<WrappedNode> {
+ return this.resumptionTargets;
+ }
+
+ private translate(node: source.node.Node): WrappedNode {
+ if (this.map.has(node)) {
+ return this.map.get(node)!;
+ }
+
+ const id = () => this.id.id(node.name);
+
+ const nodeImpl = this.implementation.node;
+
+ // Instantiate target class
+ let result: MatchResult;
+ if (node instanceof source.node.Error) {
+ result = new nodeImpl.Error(
+ new frontend.node.Error(id(), node.code, node.reason));
+ } else if (node instanceof source.node.Pause) {
+ result = new nodeImpl.Pause(
+ new frontend.node.Pause(id(), node.code, node.reason));
+ } else if (node instanceof source.node.Consume) {
+ result = new nodeImpl.Consume(
+ new frontend.node.Consume(id(), node.field));
+ } else if (node instanceof source.node.SpanStart) {
+ result = new nodeImpl.SpanStart(
+ new frontend.node.SpanStart(id(), this.spanMap.get(node.span)!,
+ this.translateSpanCode(node.span.callback)));
+ } else if (node instanceof source.node.SpanEnd) {
+ result = new nodeImpl.SpanEnd(
+ new frontend.node.SpanEnd(id(), this.spanMap.get(node.span)!,
+ this.translateSpanCode(node.span.callback)));
+ } else if (node instanceof source.node.Invoke) {
+ assert(node.code.signature === 'match' || node.code.signature === 'value',
+ 'Passing `span` callback to `invoke` is not allowed');
+ result = new nodeImpl.Invoke(
+ new frontend.node.Invoke(id(), this.translateCode(node.code)));
+ } else if (node instanceof source.node.Match) {
+ result = this.translateMatch(node);
+ } else {
+ throw new Error(`Unknown node type for "${node.name}" ${node.constructor.toString()}`);
+ }
+
+ // Initialize result
+ const otherwise = node.getOtherwiseEdge();
+
+ if (Array.isArray(result)) {
+ assert(node instanceof source.node.Match);
+ const match = node as source.node.Match;
+
+ // TODO(indutny): move this to llparse-builder?
+ assert.notStrictEqual(otherwise, undefined,
+ `Node "${node.name}" has no \`.otherwise()\``);
+
+ // Assign otherwise to every node of Trie
+ if (otherwise !== undefined) {
+ for (const child of result) {
+ if (!child.ref.otherwise) {
+ child.ref.setOtherwise(this.translate(otherwise.node),
+ otherwise.noAdvance);
+ }
+ }
+ }
+
+ // Assign transform to every node of Trie
+ const transform = this.translateTransform(match.getTransform());
+ for (const child of result) {
+ child.ref.setTransform(transform);
+ }
+
+ assert(result.length >= 1);
+ return result[0];
+ } else {
+ const single: WrappedNode = result as WrappedNode;
+ assert(single.ref instanceof frontend.node.Node);
+
+ // Break loops
+ this.map.set(node, single);
+
+ if (otherwise !== undefined) {
+ single.ref.setOtherwise(this.translate(otherwise.node),
+ otherwise.noAdvance);
+ } else {
+ // TODO(indutny): move this to llparse-builder?
+ assert(node instanceof source.node.Error,
+ `Node "${node.name}" has no \`.otherwise()\``);
+ }
+
+ if (single.ref instanceof frontend.node.Invoke) {
+ for (const edge of node) {
+ single.ref.addEdge(edge.key as number, this.translate(edge.node));
+ }
+ } else {
+ assert.strictEqual(Array.from(node).length, 0);
+ }
+
+ return single;
+ }
+ }
+
+ private registerNode(node: any): void {
+ const nodeImpl = this.implementation.node;
+
+ // Nodes with prologue check (start_pos != end_pos)
+ if (node instanceof nodeImpl.Consume ||
+ node instanceof nodeImpl.Empty ||
+ node instanceof nodeImpl.Sequence ||
+ node instanceof nodeImpl.Single ||
+ node instanceof nodeImpl.SpanStart ||
+ node instanceof nodeImpl.TableLookup) {
+ this.resumptionTargets.add(node);
+
+ // Nodes that can interrupt the execution to be resumed at different node
+ } else if (node instanceof nodeImpl.Pause ||
+ node instanceof nodeImpl.SpanEnd) {
+ this.resumptionTargets.add(node.ref.otherwise!.node);
+ }
+ }
+
+ private translateMatch(node: source.node.Match): MatchResult {
+ const trie = new Trie(node.name);
+
+ const otherwise = node.getOtherwiseEdge();
+ const trieNode = trie.build(Array.from(node));
+ if (trieNode === undefined) {
+ return new this.implementation.node.Empty(
+ new frontend.node.Empty(this.id.id(node.name)));
+ }
+
+ const children: MatchChildren = [];
+ this.translateTrie(node, trieNode, children);
+ assert(children.length >= 1);
+
+ return children;
+ }
+
+ private translateTrie(node: source.node.Match, trie: TrieNode,
+ children: MatchChildren): WrappedNode {
+ if (trie instanceof TrieEmpty) {
+ assert(this.map.has(node));
+ return this.translate(trie.node);
+ } else if (trie instanceof TrieSingle) {
+ return this.translateSingle(node, trie, children);
+ } else if (trie instanceof TrieSequence) {
+ return this.translateSequence(node, trie, children);
+ } else {
+ throw new Error('Unknown trie node');
+ }
+ }
+
+ private translateSingle(node: source.node.Match, trie: TrieSingle,
+ children: MatchChildren)
+ : IWrap<frontend.node.Match> {
+ // See if we can apply TableLookup optimization
+ const maybeTable = this.maybeTableLookup(node, trie, children);
+ if (maybeTable !== undefined) {
+ return maybeTable;
+ }
+
+ const single = new this.implementation.node.Single(
+ new frontend.node.Single(this.id.id(node.name)));
+ children.push(single);
+
+ // Break the loop
+ if (!this.map.has(node)) {
+ this.map.set(node, single);
+ }
+ for (const child of trie.children) {
+ const childNode = this.translateTrie(node, child.node, children);
+
+ single.ref.addEdge({
+ key: child.key,
+ noAdvance: child.noAdvance,
+ node: childNode,
+ value: child.node instanceof TrieEmpty ? child.node.value : undefined,
+ });
+ }
+
+ const otherwise = trie.otherwise;
+ if (otherwise) {
+ single.ref.setOtherwise(
+ this.translateTrie(node, otherwise, children),
+ true,
+ otherwise.value);
+ }
+
+ return single;
+ }
+
+ private maybeTableLookup(node: source.node.Match, trie: TrieSingle,
+ children: MatchChildren)
+ : IWrap<frontend.node.Match> | undefined {
+ if (trie.children.length < this.options.minTableSize) {
+ debug('not enough children of "%s" to allocate table, got %d need %d',
+ node.name, trie.children.length, this.options.minTableSize);
+ return undefined;
+ }
+
+ const targets: Map<source.node.Node, ITableLookupTarget> = new Map();
+
+ const bailout = !trie.children.every((child) => {
+ if (!(child.node instanceof TrieEmpty)) {
+ debug('non-leaf trie child of "%s" prevents table allocation',
+ node.name);
+ return false;
+ }
+
+ const empty: TrieEmpty = child.node;
+
+ // We can't pass values from the table yet
+ if (empty.value !== undefined) {
+ debug('value passing trie leaf of "%s" prevents table allocation',
+ node.name);
+ return false;
+ }
+
+ const target = empty.node;
+ if (!targets.has(target)) {
+ targets.set(target, {
+ keys: [ child.key ],
+ noAdvance: child.noAdvance,
+ trie: empty,
+ });
+ return true;
+ }
+
+ const existing = targets.get(target)!;
+
+ // TODO(indutny): just use it as a sub-key?
+ if (existing.noAdvance !== child.noAdvance) {
+ debug(
+ 'noAdvance mismatch in a trie leaf of "%s" prevents ' +
+ 'table allocation',
+ node.name);
+ return false;
+ }
+
+ existing.keys.push(child.key);
+ return true;
+ });
+
+ if (bailout) {
+ return undefined;
+ }
+
+ // We've width limit for this optimization
+ if (targets.size >= (1 << this.options.maxTableElemWidth)) {
+ debug('too many different trie targets of "%s" for a table allocation',
+ node.name);
+ return undefined;
+ }
+
+ const table = new this.implementation.node.TableLookup(
+ new frontend.node.TableLookup(this.id.id(node.name)));
+ children.push(table);
+
+ // Break the loop
+ if (!this.map.has(node)) {
+ this.map.set(node, table);
+ }
+
+ targets.forEach((target) => {
+ const next = this.translateTrie(node, target.trie, children);
+
+ table.ref.addEdge({
+ keys: target.keys,
+ noAdvance: target.noAdvance,
+ node: next,
+ });
+ });
+
+ debug('optimized "%s" to a table lookup node', node.name);
+ return table;
+ }
+
+ private translateSequence(node: source.node.Match, trie: TrieSequence,
+ children: MatchChildren)
+ : IWrap<frontend.node.Match> {
+ const sequence = new this.implementation.node.Sequence(
+ new frontend.node.Sequence(this.id.id(node.name), trie.select));
+ children.push(sequence);
+
+ // Break the loop
+ if (!this.map.has(node)) {
+ this.map.set(node, sequence);
+ }
+
+ const childNode = this.translateTrie(node, trie.child, children);
+
+ const value = trie.child instanceof TrieEmpty ?
+ trie.child.value : undefined;
+
+ sequence.ref.setEdge(childNode, value);
+
+ return sequence;
+ }
+
+ private translateCode(code: source.code.Code): WrappedCode {
+ const prefixed = this.codeId.id(code.name).name;
+ const codeImpl = this.implementation.code;
+
+ let res: WrappedCode;
+ if (code instanceof source.code.IsEqual) {
+ res = new codeImpl.IsEqual(
+ new frontend.code.IsEqual(prefixed, code.field, code.value));
+ } else if (code instanceof source.code.Load) {
+ res = new codeImpl.Load(
+ new frontend.code.Load(prefixed, code.field));
+ } else if (code instanceof source.code.MulAdd) {
+ // TODO(indutny): verify property type
+ const m = new frontend.code.MulAdd(prefixed, code.field, {
+ base: code.options.base,
+ max: code.options.max,
+ signed: code.options.signed === undefined ? true : code.options.signed,
+ });
+ res = new codeImpl.MulAdd(m);
+ } else if (code instanceof source.code.And) {
+ res = new codeImpl.And(
+ new frontend.code.Or(prefixed, code.field, code.value));
+ } else if (code instanceof source.code.Or) {
+ res = new codeImpl.Or(
+ new frontend.code.Or(prefixed, code.field, code.value));
+ } else if (code instanceof source.code.Store) {
+ res = new codeImpl.Store(
+ new frontend.code.Store(prefixed, code.field));
+ } else if (code instanceof source.code.Test) {
+ res = new codeImpl.Test(
+ new frontend.code.Test(prefixed, code.field, code.value));
+ } else if (code instanceof source.code.Update) {
+ res = new codeImpl.Update(
+ new frontend.code.Update(prefixed, code.field, code.value));
+
+ // External callbacks
+ } else if (code instanceof source.code.Span) {
+ res = new codeImpl.Span(new frontend.code.Span(code.name));
+ } else if (code instanceof source.code.Match) {
+ res = new codeImpl.Match(new frontend.code.Match(code.name));
+ } else if (code instanceof source.code.Value) {
+ res = new codeImpl.Value(new frontend.code.Value(code.name));
+ } else {
+ throw new Error(`Unsupported code: "${code.name}"`);
+ }
+
+ // Re-use instances to build them just once
+ if (this.codeCache.has(res.ref.cacheKey)) {
+ return this.codeCache.get(res.ref.cacheKey)!;
+ }
+
+ this.codeCache.set(res.ref.cacheKey, res);
+ return res;
+ }
+
+ private translateSpanCode(code: source.code.Span): IWrap<frontend.code.Span> {
+ return this.translateCode(code) as IWrap<frontend.code.Span>;
+ }
+
+ private translateTransform(transform?: source.transform.Transform)
+ : IWrap<frontend.transform.Transform> {
+ const transformImpl = this.implementation.transform;
+ if (transform === undefined) {
+ return new transformImpl.ID(new frontend.transform.ID());
+ } else if (transform.name === 'to_lower') {
+ return new transformImpl.ToLower(
+ new frontend.transform.ToLower());
+ } else if (transform.name === 'to_lower_unsafe') {
+ return new transformImpl.ToLowerUnsafe(
+ new frontend.transform.ToLowerUnsafe());
+ } else {
+ throw new Error(`Unsupported transform: "${transform.name}"`);
+ }
+ }
+}
diff --git a/llparse-frontend/src/implementation/code.ts b/llparse-frontend/src/implementation/code.ts
new file mode 100644
index 0000000..c467ced
--- /dev/null
+++ b/llparse-frontend/src/implementation/code.ts
@@ -0,0 +1,16 @@
+import * as code from '../code';
+import { IWrap } from '../wrap';
+
+export interface ICodeImplementation {
+ readonly And: new(c: code.And) => IWrap<code.And>;
+ readonly IsEqual: new(c: code.IsEqual) => IWrap<code.IsEqual>;
+ readonly Load: new(c: code.Load) => IWrap<code.Load>;
+ readonly Match: new(c: code.Match) => IWrap<code.Match>;
+ readonly MulAdd: new(c: code.MulAdd) => IWrap<code.MulAdd>;
+ readonly Or: new(c: code.Or) => IWrap<code.Or>;
+ readonly Span: new(c: code.Span) => IWrap<code.Span>;
+ readonly Store: new(c: code.Store) => IWrap<code.Store>;
+ readonly Test: new(c: code.Test) => IWrap<code.Test>;
+ readonly Update: new(c: code.Update) => IWrap<code.Update>;
+ readonly Value: new(c: code.Value) => IWrap<code.Value>;
+}
diff --git a/llparse-frontend/src/implementation/full.ts b/llparse-frontend/src/implementation/full.ts
new file mode 100644
index 0000000..08c4c03
--- /dev/null
+++ b/llparse-frontend/src/implementation/full.ts
@@ -0,0 +1,9 @@
+import { ICodeImplementation } from './code';
+import { INodeImplementation } from './node';
+import { ITransformImplementation } from './transform';
+
+export interface IImplementation {
+ readonly code: ICodeImplementation;
+ readonly node: INodeImplementation;
+ readonly transform: ITransformImplementation;
+}
diff --git a/llparse-frontend/src/implementation/index.ts b/llparse-frontend/src/implementation/index.ts
new file mode 100644
index 0000000..2b5411b
--- /dev/null
+++ b/llparse-frontend/src/implementation/index.ts
@@ -0,0 +1,4 @@
+export * from './code';
+export * from './full';
+export * from './node';
+export * from './transform';
diff --git a/llparse-frontend/src/implementation/node.ts b/llparse-frontend/src/implementation/node.ts
new file mode 100644
index 0000000..af0b3df
--- /dev/null
+++ b/llparse-frontend/src/implementation/node.ts
@@ -0,0 +1,15 @@
+import * as node from '../node';
+import { IWrap } from '../wrap';
+
+export interface INodeImplementation {
+ readonly Consume: new(n: node.Consume) => IWrap<node.Consume>;
+ readonly Empty: new(n: node.Empty) => IWrap<node.Empty>;
+ readonly Error: new(n: node.Error) => IWrap<node.Error>;
+ readonly Invoke: new(n: node.Invoke) => IWrap<node.Invoke>;
+ readonly Pause: new(n: node.Pause) => IWrap<node.Pause>;
+ readonly Sequence: new(n: node.Sequence) => IWrap<node.Sequence>;
+ readonly Single: new(n: node.Single) => IWrap<node.Single>;
+ readonly SpanEnd: new(n: node.SpanEnd) => IWrap<node.SpanEnd>;
+ readonly SpanStart: new(n: node.SpanStart) => IWrap<node.SpanStart>;
+ readonly TableLookup: new(n: node.TableLookup) => IWrap<node.TableLookup>;
+}
diff --git a/llparse-frontend/src/implementation/transform.ts b/llparse-frontend/src/implementation/transform.ts
new file mode 100644
index 0000000..4382284
--- /dev/null
+++ b/llparse-frontend/src/implementation/transform.ts
@@ -0,0 +1,9 @@
+import * as transform from '../transform';
+import { IWrap } from '../wrap';
+
+export interface ITransformImplementation {
+ readonly ID: new(t: transform.ID) => IWrap<transform.ID>;
+ readonly ToLower: new(t: transform.ToLower) => IWrap<transform.ToLower>;
+ readonly ToLowerUnsafe: new(t: transform.ToLowerUnsafe)
+ => IWrap<transform.ToLowerUnsafe>;
+}
diff --git a/llparse-frontend/src/namespace/frontend.ts b/llparse-frontend/src/namespace/frontend.ts
new file mode 100644
index 0000000..2f89093
--- /dev/null
+++ b/llparse-frontend/src/namespace/frontend.ts
@@ -0,0 +1,5 @@
+import * as code from '../code';
+import * as node from '../node';
+import * as transform from '../transform';
+
+export { code, node, transform };
diff --git a/llparse-frontend/src/node/base.ts b/llparse-frontend/src/node/base.ts
new file mode 100644
index 0000000..1e93c49
--- /dev/null
+++ b/llparse-frontend/src/node/base.ts
@@ -0,0 +1,46 @@
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Slot } from './slot';
+
+export interface IReadonlyOtherwiseEdge {
+ readonly node: IWrap<Node>;
+ readonly noAdvance: boolean;
+ readonly value: number | undefined;
+}
+
+interface IOtherwiseEdge {
+ node: IWrap<Node>;
+ readonly noAdvance: boolean;
+ readonly value: number | undefined;
+}
+
+export abstract class Node {
+ private privOtherwise: IOtherwiseEdge | undefined;
+ private privSlots: ReadonlyArray<Slot> | undefined;
+
+ constructor(public readonly id: IUniqueName) {
+ }
+
+ public setOtherwise(node: IWrap<Node>, noAdvance: boolean, value?: number) {
+ this.privOtherwise = { node, noAdvance, value };
+ }
+
+ public get otherwise(): IReadonlyOtherwiseEdge | undefined {
+ return this.privOtherwise;
+ }
+
+ public *getSlots() {
+ if (this.privSlots === undefined) {
+ this.privSlots = Array.from(this.buildSlots());
+ }
+
+ yield* this.privSlots;
+ }
+
+ protected *buildSlots() {
+ const otherwise = this.privOtherwise;
+ if (otherwise !== undefined) {
+ yield new Slot(otherwise.node, (value) => otherwise.node = value);
+ }
+ }
+}
diff --git a/llparse-frontend/src/node/consume.ts b/llparse-frontend/src/node/consume.ts
new file mode 100644
index 0000000..6ab49ac
--- /dev/null
+++ b/llparse-frontend/src/node/consume.ts
@@ -0,0 +1,8 @@
+import { IUniqueName } from '../utils';
+import { Node } from './base';
+
+export class Consume extends Node {
+ constructor(id: IUniqueName, readonly field: string) {
+ super(id);
+ }
+}
diff --git a/llparse-frontend/src/node/empty.ts b/llparse-frontend/src/node/empty.ts
new file mode 100644
index 0000000..45c552c
--- /dev/null
+++ b/llparse-frontend/src/node/empty.ts
@@ -0,0 +1,4 @@
+import { Node } from './base';
+
+export class Empty extends Node {
+}
diff --git a/llparse-frontend/src/node/error.ts b/llparse-frontend/src/node/error.ts
new file mode 100644
index 0000000..c4e6faf
--- /dev/null
+++ b/llparse-frontend/src/node/error.ts
@@ -0,0 +1,9 @@
+import { IUniqueName } from '../utils';
+import { Node } from './base';
+
+export class Error extends Node {
+ constructor(id: IUniqueName, public readonly code: number,
+ public readonly reason: string) {
+ super(id);
+ }
+}
diff --git a/llparse-frontend/src/node/index.ts b/llparse-frontend/src/node/index.ts
new file mode 100644
index 0000000..bd11015
--- /dev/null
+++ b/llparse-frontend/src/node/index.ts
@@ -0,0 +1,13 @@
+export * from './base';
+export * from './consume';
+export * from './empty';
+export * from './error';
+export * from './invoke';
+export * from './match';
+export * from './pause';
+export * from './sequence';
+export * from './single';
+export * from './slot';
+export * from './span-end';
+export * from './span-start';
+export * from './table-lookup';
diff --git a/llparse-frontend/src/node/invoke.ts b/llparse-frontend/src/node/invoke.ts
new file mode 100644
index 0000000..ba6ef53
--- /dev/null
+++ b/llparse-frontend/src/node/invoke.ts
@@ -0,0 +1,39 @@
+import { Code } from '../code';
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Slot } from './slot';
+
+interface IInvokeEdge {
+ readonly code: number;
+ node: IWrap<Node>;
+}
+
+export interface IReadonlyInvokeEdge {
+ readonly code: number;
+ readonly node: IWrap<Node>;
+}
+
+export class Invoke extends Node {
+ private readonly privEdges: IInvokeEdge[] = [];
+
+ constructor(id: IUniqueName, public readonly code: IWrap<Code>) {
+ super(id);
+ }
+
+ public addEdge(code: number, node: IWrap<Node>): void {
+ this.privEdges.push({ code, node });
+ }
+
+ public get edges(): ReadonlyArray<IReadonlyInvokeEdge> {
+ return this.privEdges;
+ }
+
+ protected *buildSlots() {
+ for (const edge of this.privEdges) {
+ yield new Slot(edge.node, (value) => edge.node = value);
+ }
+
+ yield* super.buildSlots();
+ }
+}
diff --git a/llparse-frontend/src/node/match.ts b/llparse-frontend/src/node/match.ts
new file mode 100644
index 0000000..8a499d3
--- /dev/null
+++ b/llparse-frontend/src/node/match.ts
@@ -0,0 +1,11 @@
+import { Transform } from '../transform';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class Match extends Node {
+ public transform?: IWrap<Transform>;
+
+ public setTransform(transform: IWrap<Transform>): void {
+ this.transform = transform;
+ }
+}
diff --git a/llparse-frontend/src/node/pause.ts b/llparse-frontend/src/node/pause.ts
new file mode 100644
index 0000000..b9923d7
--- /dev/null
+++ b/llparse-frontend/src/node/pause.ts
@@ -0,0 +1,4 @@
+import { Error as ErrorNode } from './error';
+
+export class Pause extends ErrorNode {
+}
diff --git a/llparse-frontend/src/node/sequence.ts b/llparse-frontend/src/node/sequence.ts
new file mode 100644
index 0000000..c9105b3
--- /dev/null
+++ b/llparse-frontend/src/node/sequence.ts
@@ -0,0 +1,44 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Match } from './match';
+import { Slot } from './slot';
+
+interface ISequenceEdge {
+ node: IWrap<Node>;
+ readonly value: number | undefined;
+}
+
+export interface IReadonlySequenceEdge {
+ readonly node: IWrap<Node>;
+ readonly value: number | undefined;
+}
+
+export class Sequence extends Match {
+ private privEdge?: ISequenceEdge;
+
+ constructor(id: IUniqueName, public readonly select: Buffer) {
+ super(id);
+ }
+
+ public setEdge(node: IWrap<Node>, value?: number | undefined) {
+ assert.strictEqual(this.privEdge, undefined);
+ this.privEdge = { node, value };
+ }
+
+ public get edge(): IReadonlySequenceEdge | undefined {
+ return this.privEdge;
+ }
+
+ protected *buildSlots() {
+ const edge = this.privEdge;
+ if (edge !== undefined) {
+ yield new Slot(edge.node, (value) => edge.node = value);
+ }
+
+ yield* super.buildSlots();
+ }
+}
diff --git a/llparse-frontend/src/node/single.ts b/llparse-frontend/src/node/single.ts
new file mode 100644
index 0000000..0acf715
--- /dev/null
+++ b/llparse-frontend/src/node/single.ts
@@ -0,0 +1,46 @@
+import * as assert from 'assert';
+
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Match } from './match';
+import { Slot } from './slot';
+
+interface ISingleEdge {
+ readonly key: number;
+ node: IWrap<Node>;
+ readonly noAdvance: boolean;
+ readonly value: number | undefined;
+}
+
+export interface IReadonlySingleEdge {
+ readonly key: number;
+ node: IWrap<Node>;
+ readonly noAdvance: boolean;
+ readonly value: number | undefined;
+}
+
+export class Single extends Match {
+ private readonly privEdges: ISingleEdge[] = [];
+
+ public addEdge(edge: IReadonlySingleEdge): void {
+ this.privEdges.push({
+ key: edge.key,
+ noAdvance: edge.noAdvance,
+ node: edge.node,
+ value: edge.value,
+ });
+ }
+
+ public get edges(): ReadonlyArray<IReadonlySingleEdge> {
+ return this.privEdges;
+ }
+
+ protected *buildSlots() {
+ for (const edge of this.privEdges) {
+ yield new Slot(edge.node, (value) => edge.node = value);
+ }
+
+ yield* super.buildSlots();
+ }
+}
diff --git a/llparse-frontend/src/node/slot.ts b/llparse-frontend/src/node/slot.ts
new file mode 100644
index 0000000..923da86
--- /dev/null
+++ b/llparse-frontend/src/node/slot.ts
@@ -0,0 +1,20 @@
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class Slot {
+ private privNode: IWrap<Node>;
+
+ constructor(node: IWrap<Node>,
+ private readonly privUpdate: (value: IWrap<Node>) => void) {
+ this.privNode = node;
+ }
+
+ public get node(): IWrap<Node> {
+ return this.privNode;
+ }
+
+ public set node(value: IWrap<Node>) {
+ this.privNode = value;
+ this.privUpdate(value);
+ }
+}
diff --git a/llparse-frontend/src/node/span-end.ts b/llparse-frontend/src/node/span-end.ts
new file mode 100644
index 0000000..bf8d5cc
--- /dev/null
+++ b/llparse-frontend/src/node/span-end.ts
@@ -0,0 +1,12 @@
+import { Span } from '../code';
+import { SpanField } from '../span-field';
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class SpanEnd extends Node {
+ constructor(id: IUniqueName, public readonly field: SpanField,
+ public readonly callback: IWrap<Span>) {
+ super(id);
+ }
+}
diff --git a/llparse-frontend/src/node/span-start.ts b/llparse-frontend/src/node/span-start.ts
new file mode 100644
index 0000000..89690f1
--- /dev/null
+++ b/llparse-frontend/src/node/span-start.ts
@@ -0,0 +1,12 @@
+import { Span } from '../code';
+import { SpanField } from '../span-field';
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class SpanStart extends Node {
+ constructor(id: IUniqueName, public readonly field: SpanField,
+ public readonly callback: IWrap<Span>) {
+ super(id);
+ }
+}
diff --git a/llparse-frontend/src/node/table-lookup.ts b/llparse-frontend/src/node/table-lookup.ts
new file mode 100644
index 0000000..9880fc7
--- /dev/null
+++ b/llparse-frontend/src/node/table-lookup.ts
@@ -0,0 +1,43 @@
+import * as assert from 'assert';
+
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Match } from './match';
+import { Slot } from './slot';
+
+interface ITableEdge {
+ readonly keys: ReadonlyArray<number>;
+ node: IWrap<Node>;
+ readonly noAdvance: boolean;
+}
+
+export interface IReadonlyTableEdge {
+ readonly keys: ReadonlyArray<number>;
+ readonly node: IWrap<Node>;
+ readonly noAdvance: boolean;
+}
+
+export class TableLookup extends Match {
+ private readonly privEdges: ITableEdge[] = [];
+
+ public addEdge(edge: IReadonlyTableEdge): void {
+ this.privEdges.push({
+ keys: edge.keys,
+ noAdvance: edge.noAdvance,
+ node: edge.node,
+ });
+ }
+
+ public get edges(): ReadonlyArray<IReadonlyTableEdge> {
+ return this.privEdges;
+ }
+
+ protected *buildSlots() {
+ for (const edge of this.privEdges) {
+ yield new Slot(edge.node, (value) => edge.node = value);
+ }
+
+ yield* super.buildSlots();
+ }
+}
diff --git a/llparse-frontend/src/peephole.ts b/llparse-frontend/src/peephole.ts
new file mode 100644
index 0000000..19ac13f
--- /dev/null
+++ b/llparse-frontend/src/peephole.ts
@@ -0,0 +1,52 @@
+import { Node, Empty } from './node';
+import { IWrap } from './wrap';
+
+type WrapNode = IWrap<Node>;
+type WrapList = ReadonlyArray<WrapNode>;
+
+export class Peephole {
+ public optimize(root: WrapNode, nodes: WrapList): WrapNode {
+ let changed = new Set(nodes);
+
+ while (changed.size !== 0) {
+ const previous = changed;
+ changed = new Set();
+
+ for (const node of previous) {
+ if (this.optimizeNode(node)) {
+ changed.add(node);
+ }
+ }
+ }
+
+ while (root.ref instanceof Empty) {
+ if (!root.ref.otherwise!.noAdvance) {
+ break;
+ }
+
+ root = root.ref.otherwise!.node;
+ }
+
+ return root;
+ }
+
+ public optimizeNode(node: WrapNode): boolean {
+ let changed = false;
+ for (const slot of node.ref.getSlots()) {
+ if (!(slot.node.ref instanceof Empty)) {
+ continue;
+ }
+
+ const otherwise = slot.node.ref.otherwise!;
+
+ // Node actively skips, can't optimize!
+ if (!otherwise.noAdvance) {
+ continue;
+ }
+
+ slot.node = otherwise.node;
+ changed = true;
+ }
+ return changed;
+ }
+}
diff --git a/llparse-frontend/src/span-field.ts b/llparse-frontend/src/span-field.ts
new file mode 100644
index 0000000..0652f77
--- /dev/null
+++ b/llparse-frontend/src/span-field.ts
@@ -0,0 +1,8 @@
+import { Span } from './code';
+import { IWrap } from './wrap';
+
+export class SpanField {
+ constructor(public readonly index: number,
+ public readonly callbacks: ReadonlyArray<IWrap<Span>>) {
+ }
+}
diff --git a/llparse-frontend/src/transform/base.ts b/llparse-frontend/src/transform/base.ts
new file mode 100644
index 0000000..5397326
--- /dev/null
+++ b/llparse-frontend/src/transform/base.ts
@@ -0,0 +1,4 @@
+export abstract class Transform {
+ constructor(public readonly name: string) {
+ }
+}
diff --git a/llparse-frontend/src/transform/id.ts b/llparse-frontend/src/transform/id.ts
new file mode 100644
index 0000000..d86e3c1
--- /dev/null
+++ b/llparse-frontend/src/transform/id.ts
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ID extends Transform {
+ constructor() {
+ super('id');
+ }
+}
diff --git a/llparse-frontend/src/transform/index.ts b/llparse-frontend/src/transform/index.ts
new file mode 100644
index 0000000..f103b3b
--- /dev/null
+++ b/llparse-frontend/src/transform/index.ts
@@ -0,0 +1,4 @@
+export * from './base';
+export * from './id';
+export * from './to-lower';
+export * from './to-lower-unsafe';
diff --git a/llparse-frontend/src/transform/to-lower-unsafe.ts b/llparse-frontend/src/transform/to-lower-unsafe.ts
new file mode 100644
index 0000000..99d9618
--- /dev/null
+++ b/llparse-frontend/src/transform/to-lower-unsafe.ts
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform {
+ constructor() {
+ super('to_lower_unsafe');
+ }
+}
diff --git a/llparse-frontend/src/transform/to-lower.ts b/llparse-frontend/src/transform/to-lower.ts
new file mode 100644
index 0000000..b333fce
--- /dev/null
+++ b/llparse-frontend/src/transform/to-lower.ts
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ToLower extends Transform {
+ constructor() {
+ super('to_lower');
+ }
+}
diff --git a/llparse-frontend/src/trie/empty.ts b/llparse-frontend/src/trie/empty.ts
new file mode 100644
index 0000000..aba52ea
--- /dev/null
+++ b/llparse-frontend/src/trie/empty.ts
@@ -0,0 +1,9 @@
+import { node as api } from 'llparse-builder';
+import { TrieNode } from './node';
+
+export class TrieEmpty extends TrieNode {
+ constructor(public readonly node: api.Node,
+ public readonly value: number | undefined) {
+ super();
+ }
+}
diff --git a/llparse-frontend/src/trie/index.ts b/llparse-frontend/src/trie/index.ts
new file mode 100644
index 0000000..391c6a3
--- /dev/null
+++ b/llparse-frontend/src/trie/index.ts
@@ -0,0 +1,136 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+import { Edge, node as api } from 'llparse-builder';
+
+import { TrieEmpty } from './empty';
+import { TrieNode } from './node';
+import { TrieSequence } from './sequence';
+import { ITrieSingleChild, TrieSingle } from './single';
+
+export { TrieEmpty, TrieNode, TrieSequence, TrieSingle };
+
+interface IEdge {
+ readonly key: Buffer;
+ readonly node: api.Node;
+ readonly noAdvance: boolean;
+ readonly value: number | undefined;
+}
+
+type Path = ReadonlyArray<Buffer>;
+type EdgeArray = ReadonlyArray<IEdge>;
+
+export class Trie {
+ constructor(private readonly name: string) {
+ }
+
+ public build(edges: ReadonlyArray<Edge>): undefined | TrieNode {
+ if (edges.length === 0) {
+ return undefined;
+ }
+
+ const internalEdges: IEdge[] = [];
+ for (const edge of edges) {
+ internalEdges.push({
+ key: edge.key as Buffer,
+ noAdvance: edge.noAdvance,
+ node: edge.node,
+ value: edge.value,
+ });
+ }
+
+ return this.level(internalEdges, []);
+ }
+
+ private level(edges: EdgeArray, path: Path): TrieNode {
+ const first = edges[0].key;
+ const last = edges[edges.length - 1].key;
+
+ // Leaf
+ if (edges.length === 1 && edges[0].key.length === 0) {
+ return new TrieEmpty(edges[0].node, edges[0].value);
+ }
+
+ // Find the longest common sub-string
+ let common = 0;
+ for (; common < first.length; common++) {
+ if (first[common] !== last[common]) {
+ break;
+ }
+ }
+
+ // Sequence
+ if (common > 1) {
+ return this.sequence(edges, first.slice(0, common), path);
+ }
+
+ // Single
+ return this.single(edges, path);
+ }
+
+ private slice(edges: EdgeArray, off: number): EdgeArray {
+ return edges.map((edge) => {
+ return {
+ key: edge.key.slice(off),
+ noAdvance: edge.noAdvance,
+ node: edge.node,
+ value: edge.value,
+ };
+ }).sort((a, b) => {
+ return a.key.compare(b.key);
+ });
+ }
+
+ private sequence(edges: EdgeArray, prefix: Buffer, path: Path): TrieNode {
+ const sliced = this.slice(edges, prefix.length);
+ const noAdvance = sliced.some((edge) => edge.noAdvance);
+ assert(!noAdvance);
+ const child = this.level(sliced, path.concat(prefix));
+
+ return new TrieSequence(prefix, child);
+ }
+
+ private single(edges: EdgeArray, path: Path): TrieNode {
+ // Check for duplicates
+ if (edges[0].key.length === 0) {
+ assert(path.length !== 0, `Empty root entry at "${this.name}"`);
+ assert(edges.length === 1 || edges[1].key.length !== 0,
+ `Duplicate entries in "${this.name}" at [ ${path.join(', ')} ]`);
+ }
+
+ let otherwise: TrieEmpty | undefined;
+ const keys: Map<number, IEdge[]> = new Map();
+ for (const edge of edges) {
+ if (edge.key.length === 0) {
+ otherwise = new TrieEmpty(edge.node, edge.value);
+ continue;
+ }
+ const key = edge.key[0];
+
+ if (keys.has(key)) {
+ keys.get(key)!.push(edge);
+ } else {
+ keys.set(key, [ edge ]);
+ }
+ }
+
+ const children: ITrieSingleChild[] = [];
+ keys.forEach((subEdges, key) => {
+ const sliced = this.slice(subEdges, 1);
+ const subpath = path.concat(Buffer.from([ key ]));
+
+ const noAdvance = subEdges.some((edge) => edge.noAdvance);
+ const allSame = subEdges.every((edge) => edge.noAdvance === noAdvance);
+
+ assert(allSame || subEdges.length === 0,
+ 'Conflicting `.peek()` and `.match()` entries in ' +
+ `"${this.name}" at [ ${subpath.join(', ')} ]`);
+
+ children.push({
+ key,
+ noAdvance,
+ node: this.level(sliced, subpath),
+ });
+ });
+ return new TrieSingle(children, otherwise);
+ }
+}
diff --git a/llparse-frontend/src/trie/node.ts b/llparse-frontend/src/trie/node.ts
new file mode 100644
index 0000000..31f327c
--- /dev/null
+++ b/llparse-frontend/src/trie/node.ts
@@ -0,0 +1,2 @@
+export abstract class TrieNode {
+}
diff --git a/llparse-frontend/src/trie/sequence.ts b/llparse-frontend/src/trie/sequence.ts
new file mode 100644
index 0000000..6b17e02
--- /dev/null
+++ b/llparse-frontend/src/trie/sequence.ts
@@ -0,0 +1,9 @@
+import { node as api } from 'llparse-builder';
+import { TrieNode } from './node';
+
+export class TrieSequence extends TrieNode {
+ constructor(public readonly select: Buffer,
+ public readonly child: TrieNode) {
+ super();
+ }
+}
diff --git a/llparse-frontend/src/trie/single.ts b/llparse-frontend/src/trie/single.ts
new file mode 100644
index 0000000..c984af0
--- /dev/null
+++ b/llparse-frontend/src/trie/single.ts
@@ -0,0 +1,16 @@
+import { node as api } from 'llparse-builder';
+import { TrieEmpty } from './empty';
+import { TrieNode } from './node';
+
+export interface ITrieSingleChild {
+ readonly key: number;
+ readonly noAdvance: boolean;
+ readonly node: TrieNode;
+}
+
+export class TrieSingle extends TrieNode {
+ constructor(public readonly children: ReadonlyArray<ITrieSingleChild>,
+ public readonly otherwise: TrieEmpty | undefined) {
+ super();
+ }
+}
diff --git a/llparse-frontend/src/utils/identifier.ts b/llparse-frontend/src/utils/identifier.ts
new file mode 100644
index 0000000..c9ba6ad
--- /dev/null
+++ b/llparse-frontend/src/utils/identifier.ts
@@ -0,0 +1,32 @@
+export interface IUniqueName {
+ readonly name: string;
+ readonly originalName: string;
+}
+
+export class Identifier {
+ private readonly ns: Set<string> = new Set();
+
+ constructor(private readonly prefix: string = '',
+ private readonly postfix: string = '') {
+ }
+
+ public id(name: string): IUniqueName {
+ let target = this.prefix + name + this.postfix;
+ if (this.ns.has(target)) {
+ let i = 1;
+ for (; i < this.ns.size; i++) {
+ if (!this.ns.has(target + '_' + i)) {
+ break;
+ }
+ }
+
+ target += '_' + i;
+ }
+
+ this.ns.add(target);
+ return {
+ name: target,
+ originalName: name,
+ };
+ }
+}
diff --git a/llparse-frontend/src/utils/index.ts b/llparse-frontend/src/utils/index.ts
new file mode 100644
index 0000000..06e86f1
--- /dev/null
+++ b/llparse-frontend/src/utils/index.ts
@@ -0,0 +1,19 @@
+export { Identifier, IUniqueName } from './identifier';
+
+export function toCacheKey(value: number | boolean): string {
+ if (typeof value === 'number') {
+ if (value < 0) {
+ return 'm' + (-value);
+ } else {
+ return value.toString();
+ }
+ } else if (typeof value === 'boolean') {
+ if (value === true) {
+ return 'true';
+ } else {
+ return 'false';
+ }
+ } else {
+ throw new Error(`Unsupported value: "${value}"`);
+ }
+}
diff --git a/llparse-frontend/src/wrap.ts b/llparse-frontend/src/wrap.ts
new file mode 100644
index 0000000..013adb3
--- /dev/null
+++ b/llparse-frontend/src/wrap.ts
@@ -0,0 +1,3 @@
+export interface IWrap<T> {
+ readonly ref: T;
+}
diff --git a/llparse-frontend/test/container-test.ts b/llparse-frontend/test/container-test.ts
new file mode 100644
index 0000000..28b7f1b
--- /dev/null
+++ b/llparse-frontend/test/container-test.ts
@@ -0,0 +1,46 @@
+import * as assert from 'assert';
+
+import { Builder } from 'llparse-builder';
+
+import { Container, ContainerWrap, Frontend, node } from '../src/frontend';
+import implementation from './fixtures/a-implementation';
+import { Node } from './fixtures/implementation/node/base';
+
+describe('llparse-frontend/Container', () => {
+ let b: Builder;
+ beforeEach(() => {
+ b = new Builder();
+ });
+
+ it('should translate nodes to implementation', () => {
+ const comb = new Container();
+ comb.add('a', implementation);
+ comb.add('b', implementation);
+
+ const f = new Frontend('llparse', comb.build());
+
+ const root = b.node('root');
+
+ root.match('ab', root);
+ root.match('acd', root);
+ root.match('efg', root);
+ root.otherwise(b.error(123, 'hello'));
+
+ const fRoot = f.compile(root, []).root as ContainerWrap<node.Node>;
+
+ const out: string[] = [];
+ (fRoot.get('a') as Node<node.Node>).build(out);
+
+ assert.deepStrictEqual(out, [
+ '<Single name=llparse__n_root k97=llparse__n_root_1 ' +
+ 'k101=llparse__n_root_3 otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k98=llparse__n_root ' +
+ 'k99=llparse__n_root_2 otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_2 k100=llparse__n_root ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ '<Sequence name=llparse__n_root_3 select="6667" ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ ]);
+ });
+});
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/and.ts b/llparse-frontend/test/fixtures/a-implementation/code/and.ts
new file mode 100644
index 0000000..c1df821
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/and.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class And extends Code<code.And> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/base.ts b/llparse-frontend/test/fixtures/a-implementation/code/base.ts
new file mode 100644
index 0000000..d9a7ace
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/base.ts
@@ -0,0 +1,6 @@
+export abstract class Code<T> {
+ constructor(public readonly ref: T) {
+ }
+
+ public abstract build(): string;
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/index.ts b/llparse-frontend/test/fixtures/a-implementation/code/index.ts
new file mode 100644
index 0000000..855a5cf
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/index.ts
@@ -0,0 +1,15 @@
+import { And } from './and';
+import { IsEqual } from './is-equal';
+import { Load } from './load';
+import { Match } from './match';
+import { MulAdd } from './mul-add';
+import { Or } from './or';
+import { Span } from './span';
+import { Store } from './store';
+import { Test } from './test';
+import { Update } from './update';
+import { Value } from './value';
+
+export default {
+ And, IsEqual, Load, Match, MulAdd, Or, Span, Store, Test, Update, Value,
+};
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts b/llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts
new file mode 100644
index 0000000..13a1737
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class IsEqual extends Code<code.IsEqual> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/load.ts b/llparse-frontend/test/fixtures/a-implementation/code/load.ts
new file mode 100644
index 0000000..bc97f27
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/load.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Load extends Code<code.Load> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/match.ts b/llparse-frontend/test/fixtures/a-implementation/code/match.ts
new file mode 100644
index 0000000..e933a71
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/match.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Match extends Code<code.Match> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts b/llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts
new file mode 100644
index 0000000..e06a217
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class MulAdd extends Code<code.MulAdd> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/or.ts b/llparse-frontend/test/fixtures/a-implementation/code/or.ts
new file mode 100644
index 0000000..a569db4
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/or.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Or extends Code<code.Or> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/span.ts b/llparse-frontend/test/fixtures/a-implementation/code/span.ts
new file mode 100644
index 0000000..46fc410
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/span.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Span extends Code<code.Span> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/store.ts b/llparse-frontend/test/fixtures/a-implementation/code/store.ts
new file mode 100644
index 0000000..7a1ca9f
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/store.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Store extends Code<code.Store> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/test.ts b/llparse-frontend/test/fixtures/a-implementation/code/test.ts
new file mode 100644
index 0000000..4fc8ddb
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/test.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Test extends Code<code.Test> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/update.ts b/llparse-frontend/test/fixtures/a-implementation/code/update.ts
new file mode 100644
index 0000000..16b20e2
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/update.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Update extends Code<code.Update> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/code/value.ts b/llparse-frontend/test/fixtures/a-implementation/code/value.ts
new file mode 100644
index 0000000..8e76e2a
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/code/value.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Value extends Code<code.Value> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/index.ts b/llparse-frontend/test/fixtures/a-implementation/index.ts
new file mode 100644
index 0000000..1d8d29a
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/index.ts
@@ -0,0 +1,5 @@
+import code from './code';
+import node from './node';
+import transform from './transform';
+
+export default { code, node, transform };
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/base.ts b/llparse-frontend/test/fixtures/a-implementation/node/base.ts
new file mode 100644
index 0000000..04c8285
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/base.ts
@@ -0,0 +1,38 @@
+import { ContainerWrap, node } from '../../../../src/frontend';
+
+export abstract class Node<T extends node.Node> {
+ private built: boolean = false;
+
+ constructor(public readonly ref: T) {
+ }
+
+ public build(out: string[]): void {
+ if (this.built) {
+ return;
+ }
+
+ this.built = true;
+ this.doBuild(out);
+
+ if (this.ref.otherwise !== undefined) {
+ const cwrap = this.ref.otherwise.node as ContainerWrap<T>;
+ const otherwise = cwrap.get<Node<T>>('a');
+ otherwise.build(out);
+ }
+ }
+
+ protected format(value: string): string {
+ let otherwise: string = '';
+ if (this.ref.otherwise !== undefined) {
+ const otherwiseRef = this.ref.otherwise.node.ref;
+ otherwise = ' otherwise' +
+ `${this.ref.otherwise.noAdvance ? '-no_adv' : ''}=` +
+ `${otherwiseRef.id.name}`;
+ }
+
+ return `<${this.constructor.name} name=${this.ref.id.name} ` +
+ `${value}${otherwise}/>`;
+ }
+
+ protected abstract doBuild(out: string[]): void;
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/consume.ts b/llparse-frontend/test/fixtures/a-implementation/node/consume.ts
new file mode 100644
index 0000000..cdc6cef
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/consume.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Consume extends Node<node.Consume> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(`field=${this.ref.field}`));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/empty.ts b/llparse-frontend/test/fixtures/a-implementation/node/empty.ts
new file mode 100644
index 0000000..ef1499b
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/empty.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Empty extends Node<node.Empty> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/error.ts b/llparse-frontend/test/fixtures/a-implementation/node/error.ts
new file mode 100644
index 0000000..1a4f31d
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/error.ts
@@ -0,0 +1,10 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+class ErrorNode extends Node<node.Error> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(`code=${this.ref.code} reason="${this.ref.reason}"`));
+ }
+}
+
+export { ErrorNode as Error };
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/index.ts b/llparse-frontend/test/fixtures/a-implementation/node/index.ts
new file mode 100644
index 0000000..31dbc5e
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/index.ts
@@ -0,0 +1,15 @@
+import { Consume } from './consume';
+import { Empty } from './empty';
+import { Error } from './error';
+import { Invoke } from './invoke';
+import { Pause } from './pause';
+import { Sequence } from './sequence';
+import { Single } from './single';
+import { SpanEnd } from './span-end';
+import { SpanStart } from './span-start';
+import { TableLookup } from './table-lookup';
+
+export default {
+ Consume, Empty, Error, Invoke, Pause, Sequence, Single, SpanEnd,
+ SpanStart, TableLookup,
+};
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/invoke.ts b/llparse-frontend/test/fixtures/a-implementation/node/invoke.ts
new file mode 100644
index 0000000..674be5f
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/invoke.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Invoke extends Node<node.Invoke> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/pause.ts b/llparse-frontend/test/fixtures/a-implementation/node/pause.ts
new file mode 100644
index 0000000..94da63c
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/pause.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Pause extends Node<node.Pause> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/sequence.ts b/llparse-frontend/test/fixtures/a-implementation/node/sequence.ts
new file mode 100644
index 0000000..13fd336
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/sequence.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Sequence extends Node<node.Sequence> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(`select="${this.ref.select.toString('hex')}"`));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/single.ts b/llparse-frontend/test/fixtures/a-implementation/node/single.ts
new file mode 100644
index 0000000..d7bcc72
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/single.ts
@@ -0,0 +1,18 @@
+import { ContainerWrap, node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Single extends Node<node.Single> {
+ protected doBuild(out: string[]): void {
+ const edges: string[] = [];
+ for (const edge of this.ref.edges) {
+ edges.push(`k${edge.key}${edge.noAdvance ? '-no_adv-' : ''}=` +
+ `${edge.node.ref.id.name}`);
+ }
+ out.push(this.format(edges.join(' ')));
+
+ for (const edge of this.ref.edges) {
+ const edgeNode = edge.node as ContainerWrap<node.Node>;
+ edgeNode.get<Node<node.Node>>('a').build(out);
+ }
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/span-end.ts b/llparse-frontend/test/fixtures/a-implementation/node/span-end.ts
new file mode 100644
index 0000000..dc79b81
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/span-end.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanEnd extends Node<node.SpanEnd> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/span-start.ts b/llparse-frontend/test/fixtures/a-implementation/node/span-start.ts
new file mode 100644
index 0000000..32e373c
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/span-start.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanStart extends Node<node.SpanStart> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts b/llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts
new file mode 100644
index 0000000..e6166d0
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class TableLookup extends Node<node.TableLookup> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/base.ts b/llparse-frontend/test/fixtures/a-implementation/transform/base.ts
new file mode 100644
index 0000000..96dc27d
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/transform/base.ts
@@ -0,0 +1,6 @@
+export abstract class Transform<T> {
+ constructor(public readonly ref: T) {
+ }
+
+ public abstract build(): string;
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/id.ts b/llparse-frontend/test/fixtures/a-implementation/transform/id.ts
new file mode 100644
index 0000000..e6c1adc
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/transform/id.ts
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ID extends Transform<transform.ID> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/index.ts b/llparse-frontend/test/fixtures/a-implementation/transform/index.ts
new file mode 100644
index 0000000..bed8bc9
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/transform/index.ts
@@ -0,0 +1,5 @@
+import { ID } from './id';
+import { ToLower } from './to-lower';
+import { ToLowerUnsafe } from './to-lower-unsafe';
+
+export default { ID, ToLower, ToLowerUnsafe };
diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts
new file mode 100644
index 0000000..9d175a9
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform<transform.ToLowerUnsafe> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts
new file mode 100644
index 0000000..cbe6456
--- /dev/null
+++ b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLower extends Transform<transform.ToLower> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/and.ts b/llparse-frontend/test/fixtures/implementation/code/and.ts
new file mode 100644
index 0000000..c1df821
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/and.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class And extends Code<code.And> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/base.ts b/llparse-frontend/test/fixtures/implementation/code/base.ts
new file mode 100644
index 0000000..d9a7ace
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/base.ts
@@ -0,0 +1,6 @@
+export abstract class Code<T> {
+ constructor(public readonly ref: T) {
+ }
+
+ public abstract build(): string;
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/index.ts b/llparse-frontend/test/fixtures/implementation/code/index.ts
new file mode 100644
index 0000000..855a5cf
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/index.ts
@@ -0,0 +1,15 @@
+import { And } from './and';
+import { IsEqual } from './is-equal';
+import { Load } from './load';
+import { Match } from './match';
+import { MulAdd } from './mul-add';
+import { Or } from './or';
+import { Span } from './span';
+import { Store } from './store';
+import { Test } from './test';
+import { Update } from './update';
+import { Value } from './value';
+
+export default {
+ And, IsEqual, Load, Match, MulAdd, Or, Span, Store, Test, Update, Value,
+};
diff --git a/llparse-frontend/test/fixtures/implementation/code/is-equal.ts b/llparse-frontend/test/fixtures/implementation/code/is-equal.ts
new file mode 100644
index 0000000..13a1737
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/is-equal.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class IsEqual extends Code<code.IsEqual> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/load.ts b/llparse-frontend/test/fixtures/implementation/code/load.ts
new file mode 100644
index 0000000..bc97f27
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/load.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Load extends Code<code.Load> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/match.ts b/llparse-frontend/test/fixtures/implementation/code/match.ts
new file mode 100644
index 0000000..e933a71
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/match.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Match extends Code<code.Match> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/mul-add.ts b/llparse-frontend/test/fixtures/implementation/code/mul-add.ts
new file mode 100644
index 0000000..e06a217
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/mul-add.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class MulAdd extends Code<code.MulAdd> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/or.ts b/llparse-frontend/test/fixtures/implementation/code/or.ts
new file mode 100644
index 0000000..a569db4
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/or.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Or extends Code<code.Or> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/span.ts b/llparse-frontend/test/fixtures/implementation/code/span.ts
new file mode 100644
index 0000000..46fc410
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/span.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Span extends Code<code.Span> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/store.ts b/llparse-frontend/test/fixtures/implementation/code/store.ts
new file mode 100644
index 0000000..7a1ca9f
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/store.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Store extends Code<code.Store> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/test.ts b/llparse-frontend/test/fixtures/implementation/code/test.ts
new file mode 100644
index 0000000..4fc8ddb
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/test.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Test extends Code<code.Test> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/update.ts b/llparse-frontend/test/fixtures/implementation/code/update.ts
new file mode 100644
index 0000000..16b20e2
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/update.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Update extends Code<code.Update> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/code/value.ts b/llparse-frontend/test/fixtures/implementation/code/value.ts
new file mode 100644
index 0000000..8e76e2a
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/code/value.ts
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Value extends Code<code.Value> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/index.ts b/llparse-frontend/test/fixtures/implementation/index.ts
new file mode 100644
index 0000000..1d8d29a
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/index.ts
@@ -0,0 +1,5 @@
+import code from './code';
+import node from './node';
+import transform from './transform';
+
+export default { code, node, transform };
diff --git a/llparse-frontend/test/fixtures/implementation/node/base.ts b/llparse-frontend/test/fixtures/implementation/node/base.ts
new file mode 100644
index 0000000..c9fd589
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/base.ts
@@ -0,0 +1,39 @@
+import { node } from '../../../../src/frontend';
+
+export abstract class Node<T extends node.Node> {
+ private built: boolean = false;
+
+ constructor(public readonly ref: T) {
+ }
+
+ public build(out: string[]): void {
+ if (this.built) {
+ return;
+ }
+
+ this.built = true;
+ this.doBuild(out);
+
+ if (this.ref.otherwise !== undefined) {
+ (this.ref.otherwise.node as Node<T>).build(out);
+ }
+ }
+
+ protected format(value: string): string {
+ let otherwise: string = '';
+ if (this.ref.otherwise !== undefined) {
+ const otherwiseRef = this.ref.otherwise.node.ref;
+ otherwise = ' otherwise' +
+ `${this.ref.otherwise.noAdvance ? '-no_adv' : ''}=` +
+ `${otherwiseRef.id.name}`;
+ if (this.ref.otherwise.value !== undefined) {
+ otherwise += `:${this.ref.otherwise.value}`;
+ }
+ }
+
+ return `<${this.constructor.name} name=${this.ref.id.name} ` +
+ `${value}${otherwise}/>`;
+ }
+
+ protected abstract doBuild(out: string[]): void;
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/consume.ts b/llparse-frontend/test/fixtures/implementation/node/consume.ts
new file mode 100644
index 0000000..cdc6cef
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/consume.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Consume extends Node<node.Consume> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(`field=${this.ref.field}`));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/empty.ts b/llparse-frontend/test/fixtures/implementation/node/empty.ts
new file mode 100644
index 0000000..ef1499b
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/empty.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Empty extends Node<node.Empty> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/error.ts b/llparse-frontend/test/fixtures/implementation/node/error.ts
new file mode 100644
index 0000000..1a4f31d
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/error.ts
@@ -0,0 +1,10 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+class ErrorNode extends Node<node.Error> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(`code=${this.ref.code} reason="${this.ref.reason}"`));
+ }
+}
+
+export { ErrorNode as Error };
diff --git a/llparse-frontend/test/fixtures/implementation/node/index.ts b/llparse-frontend/test/fixtures/implementation/node/index.ts
new file mode 100644
index 0000000..31dbc5e
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/index.ts
@@ -0,0 +1,15 @@
+import { Consume } from './consume';
+import { Empty } from './empty';
+import { Error } from './error';
+import { Invoke } from './invoke';
+import { Pause } from './pause';
+import { Sequence } from './sequence';
+import { Single } from './single';
+import { SpanEnd } from './span-end';
+import { SpanStart } from './span-start';
+import { TableLookup } from './table-lookup';
+
+export default {
+ Consume, Empty, Error, Invoke, Pause, Sequence, Single, SpanEnd,
+ SpanStart, TableLookup,
+};
diff --git a/llparse-frontend/test/fixtures/implementation/node/invoke.ts b/llparse-frontend/test/fixtures/implementation/node/invoke.ts
new file mode 100644
index 0000000..674be5f
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/invoke.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Invoke extends Node<node.Invoke> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/pause.ts b/llparse-frontend/test/fixtures/implementation/node/pause.ts
new file mode 100644
index 0000000..94da63c
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/pause.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Pause extends Node<node.Pause> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/sequence.ts b/llparse-frontend/test/fixtures/implementation/node/sequence.ts
new file mode 100644
index 0000000..bb745f5
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/sequence.ts
@@ -0,0 +1,15 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Sequence extends Node<node.Sequence> {
+ protected doBuild(out: string[]): void {
+ let str = `select="${this.ref.select.toString('hex')}" ` +
+ `edge="${this.ref.edge!.node.ref.id.name}"`;
+ if (this.ref.edge!.value !== undefined) {
+ str += `:${this.ref.edge!.value}`;
+ }
+ out.push(this.format(str));
+ const edgeNode = this.ref.edge!.node as Node<node.Node>;
+ edgeNode.build(out);
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/single.ts b/llparse-frontend/test/fixtures/implementation/node/single.ts
new file mode 100644
index 0000000..b24ef93
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/single.ts
@@ -0,0 +1,22 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Single extends Node<node.Single> {
+ protected doBuild(out: string[]): void {
+ const edges: string[] = [];
+ for (const edge of this.ref.edges) {
+ let str = `k${edge.key}${edge.noAdvance ? '-no_adv-' : ''}=` +
+ `${edge.node.ref.id.name}`;
+ if (edge.value !== undefined) {
+ str += `:${edge.value}`;
+ }
+ edges.push(str);
+ }
+ out.push(this.format(edges.join(' ')));
+
+ for (const edge of this.ref.edges) {
+ const edgeNode = edge.node as Node<node.Node>;
+ edgeNode.build(out);
+ }
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/span-end.ts b/llparse-frontend/test/fixtures/implementation/node/span-end.ts
new file mode 100644
index 0000000..dc79b81
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/span-end.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanEnd extends Node<node.SpanEnd> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/span-start.ts b/llparse-frontend/test/fixtures/implementation/node/span-start.ts
new file mode 100644
index 0000000..32e373c
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/span-start.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanStart extends Node<node.SpanStart> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/node/table-lookup.ts b/llparse-frontend/test/fixtures/implementation/node/table-lookup.ts
new file mode 100644
index 0000000..e6166d0
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/node/table-lookup.ts
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class TableLookup extends Node<node.TableLookup> {
+ protected doBuild(out: string[]): void {
+ out.push(this.format(''));
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/transform/base.ts b/llparse-frontend/test/fixtures/implementation/transform/base.ts
new file mode 100644
index 0000000..96dc27d
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/transform/base.ts
@@ -0,0 +1,6 @@
+export abstract class Transform<T> {
+ constructor(public readonly ref: T) {
+ }
+
+ public abstract build(): string;
+}
diff --git a/llparse-frontend/test/fixtures/implementation/transform/id.ts b/llparse-frontend/test/fixtures/implementation/transform/id.ts
new file mode 100644
index 0000000..e6c1adc
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/transform/id.ts
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ID extends Transform<transform.ID> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/transform/index.ts b/llparse-frontend/test/fixtures/implementation/transform/index.ts
new file mode 100644
index 0000000..bed8bc9
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/transform/index.ts
@@ -0,0 +1,5 @@
+import { ID } from './id';
+import { ToLower } from './to-lower';
+import { ToLowerUnsafe } from './to-lower-unsafe';
+
+export default { ID, ToLower, ToLowerUnsafe };
diff --git a/llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts b/llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts
new file mode 100644
index 0000000..9d175a9
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform<transform.ToLowerUnsafe> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/fixtures/implementation/transform/to-lower.ts b/llparse-frontend/test/fixtures/implementation/transform/to-lower.ts
new file mode 100644
index 0000000..cbe6456
--- /dev/null
+++ b/llparse-frontend/test/fixtures/implementation/transform/to-lower.ts
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLower extends Transform<transform.ToLower> {
+ public build(): string {
+ return '';
+ }
+}
diff --git a/llparse-frontend/test/frontend-test.ts b/llparse-frontend/test/frontend-test.ts
new file mode 100644
index 0000000..69e075c
--- /dev/null
+++ b/llparse-frontend/test/frontend-test.ts
@@ -0,0 +1,187 @@
+import * as assert from 'assert';
+
+import * as source from 'llparse-builder';
+
+import { Frontend, node } from '../src/frontend';
+import implementation from './fixtures/implementation';
+import { Node } from './fixtures/implementation/node/base';
+
+function checkNodes(f: Frontend, root: source.node.Node,
+ expected: ReadonlyArray<string>) {
+ const fRoot = f.compile(root, []).root as Node<node.Node>;
+
+ const out: string[] = [];
+ fRoot.build(out);
+
+ assert.deepStrictEqual(out, expected);
+
+ return fRoot;
+}
+
+function checkResumptionTargets(f: Frontend, expected: ReadonlyArray<string>) {
+ const targets = Array.from(f.getResumptionTargets()).map((t) => {
+ return t.ref.id.name;
+ });
+
+ assert.deepStrictEqual(targets, expected);
+}
+
+describe('llparse-frontend', () => {
+ let b: source.Builder;
+ let f: Frontend;
+ beforeEach(() => {
+ b = new source.Builder();
+ f = new Frontend('llparse', implementation);
+ });
+
+ it('should translate nodes to implementation', () => {
+ const root = b.node('root');
+
+ root.match('ab', root);
+ root.match('acd', root);
+ root.match('efg', root);
+ root.otherwise(b.error(123, 'hello'));
+
+ checkNodes(f, root, [
+ '<Single name=llparse__n_root k97=llparse__n_root_1 ' +
+ 'k101=llparse__n_root_3 otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k98=llparse__n_root ' +
+ 'k99=llparse__n_root_2 otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_2 k100=llparse__n_root ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ '<Sequence name=llparse__n_root_3 select="6667" ' +
+ 'edge=\"llparse__n_root\" ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_root_1',
+ 'llparse__n_root_3',
+ 'llparse__n_root_2',
+ ]);
+ });
+
+ it('should do peephole optimization', () => {
+ const root = b.node('root');
+ const root1 = b.node('a');
+ const root2 = b.node('b');
+ const node1 = b.node('c');
+ const node2 = b.node('d');
+
+ root.otherwise(root1);
+ root1.otherwise(root2);
+ root2.skipTo(node1);
+ node1.otherwise(node2);
+ node2.otherwise(root);
+
+ checkNodes(f, root, [
+ '<Empty name=llparse__n_b otherwise=llparse__n_b/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_b',
+ ]);
+ });
+
+ it('should generate proper resumption targets', () => {
+ b.property('i64', 'counter');
+
+ const root = b.node('root');
+ const end = b.node('end');
+ const store = b.invoke(b.code.store('counter'));
+
+ root.select({ a: 1, b: 2 }, store);
+ root.otherwise(b.error(1, 'okay'));
+
+ store.otherwise(end);
+
+ end.match('ohai', root);
+ end.match('paus', b.pause(1, 'paused').otherwise(
+ b.pause(2, 'paused').otherwise(root)));
+ end.otherwise(b.error(2, 'ohai'));
+
+ checkNodes(f, root, [
+ '<Single name=llparse__n_root k97=llparse__n_invoke_store_counter:1 ' +
+ 'k98=llparse__n_invoke_store_counter:2 ' +
+ 'otherwise-no_adv=llparse__n_error_1/>',
+ '<Invoke name=llparse__n_invoke_store_counter ' +
+ 'otherwise-no_adv=llparse__n_end/>',
+ '<Single name=llparse__n_end k111=llparse__n_end_1 ' +
+ 'k112=llparse__n_end_2 otherwise-no_adv=llparse__n_error/>',
+ '<Sequence name=llparse__n_end_1 select="686169" ' +
+ 'edge="llparse__n_root" otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=2 reason="ohai"/>',
+ '<Sequence name=llparse__n_end_2 select="617573" ' +
+ 'edge="llparse__n_pause" otherwise-no_adv=llparse__n_error/>',
+ '<Pause name=llparse__n_pause otherwise-no_adv=llparse__n_pause_1/>',
+ '<Pause name=llparse__n_pause_1 otherwise-no_adv=llparse__n_root/>',
+ '<ErrorNode name=llparse__n_error_1 code=1 reason="okay"/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_end',
+ 'llparse__n_end_1',
+ 'llparse__n_end_2',
+ 'llparse__n_pause_1',
+ ]);
+ });
+
+ it('should translate Span code into Span', () => {
+ const root = b.invoke(b.code.span('my_span'));
+ root.otherwise(b.error(1, 'okay'));
+
+ const fRoot = checkNodes(f, root, [
+ '<Invoke name=llparse__n_invoke_my_span ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=1 reason="okay"/>',
+ ]);
+
+ assert((fRoot.ref as any).code instanceof implementation.code.Span);
+ });
+
+ it('should translate overlapping matches', () => {
+ const root = b.node('root');
+
+ root.match('ab', root);
+ root.match('abc', root);
+ root.otherwise(b.error(123, 'hello'));
+
+ checkNodes(f, root, [
+ '<Sequence name=llparse__n_root select="6162" edge="llparse__n_root_1" otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k99=llparse__n_root otherwise-no_adv=llparse__n_root/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_root_1',
+ ]);
+ });
+
+ it('should translate overlapping matches with values', () => {
+ const root = b.node('root');
+ const store = b.invoke(b.code.store('counter'));
+
+ root.select({
+ ab: 1,
+ abc: 2,
+ }, store);
+ store.otherwise(root);
+ root.otherwise(b.error(123, 'hello'));
+
+ checkNodes(f, root, [
+ '<Sequence name=llparse__n_root select="6162" edge="llparse__n_root_1" otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k99=llparse__n_invoke_store_counter:2 otherwise-no_adv=llparse__n_invoke_store_counter:1/>',
+ '<Invoke name=llparse__n_invoke_store_counter otherwise-no_adv=llparse__n_root/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_root_1',
+ ]);
+ });
+});
diff --git a/llparse-frontend/tsconfig.json b/llparse-frontend/tsconfig.json
new file mode 100644
index 0000000..01ec7c2
--- /dev/null
+++ b/llparse-frontend/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "target": "es2017",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./lib",
+ "declaration": true,
+ "pretty": true,
+ "sourceMap": true
+ },
+ "include": [
+ "src/**/*.ts"
+ ]
+}
diff --git a/llparse-frontend/tslint.json b/llparse-frontend/tslint.json
new file mode 100644
index 0000000..24fec09
--- /dev/null
+++ b/llparse-frontend/tslint.json
@@ -0,0 +1,16 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "no-bitwise": null,
+ "max-line-length": [true, 80],
+ "max-classes-per-file": [true, 1, "exclude-class-expressions"],
+ "quotemark": [
+ true, "single", "avoid-escape", "avoid-template"
+ ]
+ },
+ "rulesDirectory": []
+}
diff --git a/llparse/.gitignore b/llparse/.gitignore
new file mode 100644
index 0000000..88b2771
--- /dev/null
+++ b/llparse/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+npm-debug.log
+lib/
+test/tmp/
diff --git a/llparse/.travis.yml b/llparse/.travis.yml
new file mode 100644
index 0000000..b381e1b
--- /dev/null
+++ b/llparse/.travis.yml
@@ -0,0 +1,6 @@
+sudo: false
+language: node_js
+node_js:
+ - "stable"
+script:
+ CFLAGS="-O0" npm test
diff --git a/llparse/CNAME b/llparse/CNAME
new file mode 100644
index 0000000..e39566e
--- /dev/null
+++ b/llparse/CNAME
@@ -0,0 +1 @@
+llparse.org \ No newline at end of file
diff --git a/llparse/CODE_OF_CONDUCT.md b/llparse/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4c21140
--- /dev/null
+++ b/llparse/CODE_OF_CONDUCT.md
@@ -0,0 +1,4 @@
+# Code of Conduct
+
+* [Node.js Code of Conduct](https://github.com/nodejs/admin/blob/master/CODE_OF_CONDUCT.md)
+* [Node.js Moderation Policy](https://github.com/nodejs/admin/blob/master/Moderation-Policy.md)
diff --git a/llparse/LICENSE-MIT b/llparse/LICENSE-MIT
new file mode 100644
index 0000000..6c1512d
--- /dev/null
+++ b/llparse/LICENSE-MIT
@@ -0,0 +1,22 @@
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/llparse/README.md b/llparse/README.md
new file mode 100644
index 0000000..afbe4aa
--- /dev/null
+++ b/llparse/README.md
@@ -0,0 +1,86 @@
+# llparse
+[![Build Status](https://secure.travis-ci.org/nodejs/llparse.svg)](http://travis-ci.org/nodejs/llparse)
+[![NPM version](https://badge.fury.io/js/llparse.svg)](https://badge.fury.io/js/llparse)
+
+An API for compiling an incremental parser into a C output.
+
+## Usage
+
+```ts
+import { LLParse } from 'llparse';
+
+const p = new LLParse('http_parser');
+
+const method = p.node('method');
+const beforeUrl = p.node('before_url');
+const urlSpan = p.span(p.code.span('on_url'));
+const url = p.node('url');
+const http = p.node('http');
+
+// Add custom uint8_t property to the state
+p.property('i8', 'method');
+
+// Store method inside a custom property
+const onMethod = p.invoke(p.code.store('method'), beforeUrl);
+
+// Invoke custom C function
+const complete = p.invoke(p.code.match('on_complete'), {
+ // Restart
+ 0: method
+}, p.error(4, '`on_complete` error'));
+
+method
+ .select({
+ 'HEAD': 0, 'GET': 1, 'POST': 2, 'PUT': 3,
+ 'DELETE': 4, 'OPTIONS': 5, 'CONNECT': 6,
+ 'TRACE': 7, 'PATCH': 8
+ }, onMethod)
+ .otherwise(p.error(5, 'Expected method'));
+
+beforeUrl
+ .match(' ', beforeUrl)
+ .otherwise(urlSpan.start(url));
+
+url
+ .peek(' ', urlSpan.end(http))
+ .skipTo(url);
+
+http
+ .match(' HTTP/1.1\r\n\r\n', complete)
+ .otherwise(p.error(6, 'Expected HTTP/1.1 and two newlines'));
+
+const artifacts = p.build(method);
+console.log('----- C -----');
+console.log(artifacts.c); // string
+console.log('----- C END -----');
+console.log('----- HEADER -----');
+console.log(artifacts.header);
+console.log('----- HEADER END -----');
+```
+
+#### LICENSE
+
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2020.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+[3]: https://llvm.org/docs/LangRef.html
diff --git a/llparse/_config.yml b/llparse/_config.yml
new file mode 100644
index 0000000..1885487
--- /dev/null
+++ b/llparse/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-midnight \ No newline at end of file
diff --git a/llparse/examples/http/.gitignore b/llparse/examples/http/.gitignore
new file mode 100644
index 0000000..fcfe02e
--- /dev/null
+++ b/llparse/examples/http/.gitignore
@@ -0,0 +1,6 @@
+http
+*.c
+*.ll
+*.h
+*.o
+*.dSYM
diff --git a/llparse/examples/http/Makefile b/llparse/examples/http/Makefile
new file mode 100644
index 0000000..323d2e3
--- /dev/null
+++ b/llparse/examples/http/Makefile
@@ -0,0 +1,11 @@
+CC ?= clang
+
+all: http
+
+http: main.c http_parser.bc
+ $(CC) -g3 -flto -Os -fvisibility=hidden -Wall -I. http_parser.c main.c -o $@
+
+http_parser.bc: index.ts
+ npx ts-node $<
+
+.PHONY = all
diff --git a/llparse/examples/http/index.ts b/llparse/examples/http/index.ts
new file mode 100644
index 0000000..dc7f28a
--- /dev/null
+++ b/llparse/examples/http/index.ts
@@ -0,0 +1,51 @@
+import { LLParse } from '../../src/api';
+
+const p = new LLParse('http_parser');
+
+const method = p.node('method');
+const beforeUrl = p.node('before_url');
+const urlSpan = p.span(p.code.span('on_url'));
+const url = p.node('url');
+const http = p.node('http');
+
+// Add custom uint8_t property to the state
+p.property('i8', 'method');
+
+// Store method inside a custom property
+const onMethod = p.invoke(p.code.store('method'), beforeUrl);
+
+// Invoke custom C function
+const complete = p.invoke(p.code.match('on_complete'), {
+ // Restart
+ 0: method
+}, p.error(4, '`on_complete` error'));
+
+method
+ .select({
+ 'HEAD': 0, 'GET': 1, 'POST': 2, 'PUT': 3,
+ 'DELETE': 4, 'OPTIONS': 5, 'CONNECT': 6,
+ 'TRACE': 7, 'PATCH': 8
+ }, onMethod)
+ .otherwise(p.error(5, 'Expected method'));
+
+beforeUrl
+ .match(' ', beforeUrl)
+ .otherwise(urlSpan.start(url));
+
+url
+ .peek(' ', urlSpan.end(http))
+ .skipTo(url);
+
+http
+ .match(' HTTP/1.1\r\n\r\n', complete)
+ .match(' HTTP/1.1\n\n', complete)
+ .otherwise(p.error(6, 'Expected HTTP/1.1 and two newlines'));
+
+// Build
+
+const fs = require('fs');
+const path = require('path');
+
+const artifacts = p.build(method);
+fs.writeFileSync(path.join(__dirname, 'http_parser.h'), artifacts.header);
+fs.writeFileSync(path.join(__dirname, 'http_parser.c'), artifacts.c);
diff --git a/llparse/examples/http/main.c b/llparse/examples/http/main.c
new file mode 100644
index 0000000..4721a19
--- /dev/null
+++ b/llparse/examples/http/main.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "http_parser.h"
+
+int on_url(http_parser_t* s, const char* p, const char* endp) {
+ if (p == endp)
+ return 0;
+
+ fprintf(stdout, "method=%d url_part=\"%.*s\"\n", s->method,
+ (int) (endp - p), p);
+ return 0;
+}
+
+
+int on_complete(http_parser_t* s, const char* p, const char* endp) {
+ fprintf(stdout, "on_complete\n");
+ return 0;
+}
+
+
+int main(int argc, char** argv) {
+ http_parser_t s;
+
+ http_parser_init(&s);
+
+ for (;;) {
+ char buf[16384];
+ const char* input;
+ const char* endp;
+ int code;
+
+ input = fgets(buf, sizeof(buf), stdin);
+ if (input == NULL)
+ break;
+
+ endp = input + strlen(input);
+ code = http_parser_execute(&s, input, endp);
+ if (code != 0) {
+ fprintf(stderr, "code=%d error=%d reason=%s\n", code, s.error, s.reason);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/llparse/package-lock.json b/llparse/package-lock.json
new file mode 100644
index 0000000..e4c2b29
--- /dev/null
+++ b/llparse/package-lock.json
@@ -0,0 +1,1802 @@
+{
+ "name": "llparse",
+ "version": "7.1.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
+ "dev": true
+ },
+ "@types/mocha": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
+ "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "14.11.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz",
+ "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==",
+ "dev": true
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array.prototype.map": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
+ "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.4"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+ "dev": true
+ },
+ "binary-search": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
+ "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+ },
+ "bitcode": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/bitcode/-/bitcode-1.2.0.tgz",
+ "integrity": "sha512-cWgZK/ri/1ZUJ+UKEwP9Cqw10WY5wHz+boMxVO4vvc0btmxa2tMc2m2Zk9HYdCyx4b5+sgQM1/NCJPTIPO1XOw==",
+ "dev": true,
+ "requires": {
+ "bitcode-builder": "^1.2.0"
+ }
+ },
+ "bitcode-builder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/bitcode-builder/-/bitcode-builder-1.2.0.tgz",
+ "integrity": "sha512-biuJIhrog5d1IFMaKtHMJ8PJ1L3zxiWdclwYErjOBWf8Gwyqa4XwflvMufzcQw/OUeAArO1AqOrqsOFsWJ94OA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "chokidar": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+ "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.4.0"
+ }
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true
+ },
+ "es-get-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
+ "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
+ "dev": true,
+ "requires": {
+ "es-abstract": "^1.17.4",
+ "has-symbols": "^1.0.1",
+ "is-arguments": "^1.0.4",
+ "is-map": "^2.0.1",
+ "is-set": "^2.0.1",
+ "is-string": "^1.0.5",
+ "isarray": "^2.0.5"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esm": {
+ "version": "3.2.25",
+ "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz",
+ "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
+ "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
+ "dev": true
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-set": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
+ "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
+ "dev": true
+ },
+ "is-string": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+ "dev": true
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "iterate-iterator": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
+ "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
+ "dev": true
+ },
+ "iterate-value": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
+ "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
+ "dev": true,
+ "requires": {
+ "es-get-iterator": "^1.0.2",
+ "iterate-iterator": "^1.0.1"
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "llparse": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/llparse/-/llparse-6.4.0.tgz",
+ "integrity": "sha512-ySA+bj2wOLXrKmohAVMw0Nq84oHDPLdg+sUx4+VeSk1U72MEKfKAXS7zh82n15BRjWc/cVgWBN9RQAFdgk0g5Q==",
+ "dev": true,
+ "requires": {
+ "bitcode": "^1.2.0",
+ "debug": "^3.2.6",
+ "llparse-frontend": "^1.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "llparse-frontend": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/llparse-frontend/-/llparse-frontend-1.4.0.tgz",
+ "integrity": "sha512-lUpGvGU9MDPb3k4Wbb0S7FgpceCirXVeFQQZjsYWB3fIEGU0Q6IEiTO91J6MLLN75gsxvGiWZaKVnmcHb7jh6g==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.6",
+ "llparse-builder": "^1.3.2"
+ }
+ }
+ }
+ },
+ "llparse-builder": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/llparse-builder/-/llparse-builder-1.4.0.tgz",
+ "integrity": "sha512-mu0/zgAc1KdD6r+tjmRvF+YgoToQvBun4iXISRfSmx66b5qurckRpYjzBUYpHn0XVqKPRrGg86gMQKv8ogY3Rw==",
+ "dev": true,
+ "requires": {
+ "@types/debug": "0.0.30",
+ "binary-search": "^1.3.6",
+ "debug": "^3.2.6"
+ },
+ "dependencies": {
+ "@types/debug": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-0.0.30.tgz",
+ "integrity": "sha512-orGL5LXERPYsLov6CWs3Fh6203+dXzJkR7OnddIr2514Hsecwc8xRpzCapshBbKFImCsvS/mk6+FWiN5LyZJAQ==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "llparse-frontend": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/llparse-frontend/-/llparse-frontend-3.0.0.tgz",
+ "integrity": "sha512-G/o0Po2C+G5OtP8MJeQDjDf5qwDxcO7K6x4r6jqGsJwxk7yblbJnRqpmye7G/lZ8dD0Hv5neY4/KB5BhDmEc9Q==",
+ "requires": {
+ "debug": "^3.2.6",
+ "llparse-builder": "^1.5.2"
+ },
+ "dependencies": {
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "llparse-builder": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/llparse-builder/-/llparse-builder-1.5.2.tgz",
+ "integrity": "sha512-i862UNC3YUEdlfK/NUCJxlKjtWjgAI9AJXDRgjcfRHfwFt4Sf8eFPTRsc91/2R9MBZ0kyFdfhi8SVhMsZf1gNQ==",
+ "requires": {
+ "@types/debug": "4.1.5 ",
+ "binary-search": "^1.3.6",
+ "debug": "^4.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ }
+ }
+ }
+ }
+ },
+ "llparse-test-fixture": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/llparse-test-fixture/-/llparse-test-fixture-5.0.1.tgz",
+ "integrity": "sha512-BrnS70lxODcTXttLkfoSqn8DPbNuuSLFR48JnwxLimFkr8QRNBVbUku+bumIIo5Z7gAbIGNQXDOiSi2crMzS8Q==",
+ "dev": true,
+ "requires": {
+ "esm": "^3.2.25",
+ "llparse": "^6.4.0",
+ "yargs": "^15.4.1"
+ }
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "log-symbols": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+ "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "mocha": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz",
+ "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.4.2",
+ "debug": "4.1.1",
+ "diff": "4.0.2",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.1.6",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.14.0",
+ "log-symbols": "4.0.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.2",
+ "object.assign": "4.1.0",
+ "promise.allsettled": "1.0.2",
+ "serialize-javascript": "4.0.0",
+ "strip-json-comments": "3.0.1",
+ "supports-color": "7.1.0",
+ "which": "2.0.2",
+ "wide-align": "1.1.3",
+ "workerpool": "6.0.0",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "p-limit": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ },
+ "dependencies": {
+ "p-limit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ }
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "promise.allsettled": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
+ "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
+ "dev": true,
+ "requires": {
+ "array.prototype.map": "^1.0.1",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "iterate-value": "^1.0.0"
+ }
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ }
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "ts-node": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+ "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.17",
+ "yn": "3.1.1"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "tslint": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+ "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.3",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.13.0",
+ "tsutils": "^2.29.0"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ }
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "typescript": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
+ "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "workerpool": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
+ "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
+ "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
+ "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.3.1",
+ "decamelize": "^1.2.0",
+ "flat": "^4.1.0",
+ "is-plain-obj": "^1.1.0",
+ "yargs": "^14.2.3"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "yargs": {
+ "version": "14.2.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
+ "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^15.0.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
+ "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/llparse/package.json b/llparse/package.json
new file mode 100644
index 0000000..ee35dc4
--- /dev/null
+++ b/llparse/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "llparse",
+ "version": "7.1.1",
+ "description": "Compile incremental parsers to C code",
+ "main": "lib/api.js",
+ "types": "lib/api.d.ts",
+ "files": [
+ "lib",
+ "src"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "clean": "rm -rf lib",
+ "prepare": "npm run clean && npm run build",
+ "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
+ "fix-lint": "npm run lint -- --fix",
+ "mocha": "mocha --timeout=10000 -r ts-node/register/type-check --reporter spec test/*-test.ts",
+ "test": "npm run mocha && npm run lint"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+ssh://git@github.com/nodejs/llparse.git"
+ },
+ "keywords": [
+ "llparse",
+ "compiler"
+ ],
+ "author": "Fedor Indutny <fedor@indutny.com> (http://darksi.de/)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/nodejs/llparse/issues"
+ },
+ "homepage": "https://github.com/nodejs/llparse#readme",
+ "devDependencies": {
+ "@types/debug": "^4.1.5",
+ "@types/mocha": "^8.0.3",
+ "@types/node": "^14.11.8",
+ "esm": "^3.2.25",
+ "llparse-test-fixture": "^5.0.1",
+ "mocha": "^8.1.3",
+ "ts-node": "^9.0.0",
+ "tslint": "^6.1.3",
+ "typescript": "^4.0.3"
+ },
+ "dependencies": {
+ "debug": "^4.2.0",
+ "llparse-frontend": "^3.0.0"
+ }
+}
diff --git a/llparse/src/api.ts b/llparse/src/api.ts
new file mode 100644
index 0000000..a34f5bc
--- /dev/null
+++ b/llparse/src/api.ts
@@ -0,0 +1,47 @@
+import * as frontend from 'llparse-frontend';
+
+import source = frontend.source;
+
+import { Compiler, ICompilerOptions, ICompilerResult } from './compiler';
+
+export { source, ICompilerOptions, ICompilerResult };
+
+// TODO(indutny): API for disabling/short-circuiting spans
+
+/**
+ * LLParse graph builder and compiler.
+ */
+export class LLParse extends source.Builder {
+ /**
+ * The prefix controls the names of methods and state struct in generated
+ * public C headers:
+ *
+ * ```c
+ * // state struct
+ * struct PREFIX_t {
+ * ...
+ * }
+ *
+ * int PREFIX_init(PREFIX_t* state);
+ * int PREFIX_execute(PREFIX_t* state, const char* p, const char* endp);
+ * ```
+ *
+ * @param prefix Prefix to be used when generating public API.
+ */
+ constructor(private readonly prefix: string = 'llparse') {
+ super();
+ }
+
+ /**
+ * Compile LLParse graph to the C code and C headers
+ *
+ * @param root Root node of the parse graph (see `.node()`)
+ * @param options Compiler options.
+ */
+ public build(root: source.node.Node, options: ICompilerOptions = {})
+ : ICompilerResult {
+ const c = new Compiler(this.prefix, options);
+
+ return c.compile(root, this.properties);
+ }
+}
diff --git a/llparse/src/compiler/header-builder.ts b/llparse/src/compiler/header-builder.ts
new file mode 100644
index 0000000..9f5bee7
--- /dev/null
+++ b/llparse/src/compiler/header-builder.ts
@@ -0,0 +1,80 @@
+import * as frontend from 'llparse-frontend';
+import source = frontend.source;
+
+export interface IHeaderBuilderOptions {
+ readonly prefix: string;
+ readonly headerGuard?: string;
+ readonly properties: ReadonlyArray<source.Property>;
+ readonly spans: ReadonlyArray<frontend.SpanField>;
+}
+
+export class HeaderBuilder {
+ public build(options: IHeaderBuilderOptions): string {
+ let res = '';
+ const PREFIX = options.prefix.toUpperCase().replace(/[^a-z]/gi, '_');
+ const DEFINE = options.headerGuard === undefined ?
+ `INCLUDE_${PREFIX}_H_` : options.headerGuard;
+
+ res += `#ifndef ${DEFINE}\n`;
+ res += `#define ${DEFINE}\n`;
+ res += '#ifdef __cplusplus\n';
+ res += 'extern "C" {\n';
+ res += '#endif\n';
+ res += '\n';
+
+ res += '#include <stdint.h>\n';
+ res += '\n';
+
+ // Structure
+ res += `typedef struct ${options.prefix}_s ${options.prefix}_t;\n`;
+ res += `struct ${options.prefix}_s {\n`;
+ res += ' int32_t _index;\n';
+
+ for (const [ index, field ] of options.spans.entries()) {
+ res += ` void* _span_pos${index};\n`;
+ if (field.callbacks.length > 1) {
+ res += ` void* _span_cb${index};\n`;
+ }
+ }
+
+ res += ' int32_t error;\n';
+ res += ' const char* reason;\n';
+ res += ' const char* error_pos;\n';
+ res += ' void* data;\n';
+ res += ' void* _current;\n';
+
+ for (const prop of options.properties) {
+ let ty: string;
+ if (prop.ty === 'i8') {
+ ty = 'uint8_t';
+ } else if (prop.ty === 'i16') {
+ ty = 'uint16_t';
+ } else if (prop.ty === 'i32') {
+ ty = 'uint32_t';
+ } else if (prop.ty === 'i64') {
+ ty = 'uint64_t';
+ } else if (prop.ty === 'ptr') {
+ ty = 'void*';
+ } else {
+ throw new Error(
+ `Unknown state property type: "${prop.ty}"`);
+ }
+ res += ` ${ty} ${prop.name};\n`;
+ }
+ res += '};\n';
+
+ res += '\n';
+
+ res += `int ${options.prefix}_init(${options.prefix}_t* s);\n`;
+ res += `int ${options.prefix}_execute(${options.prefix}_t* s, ` +
+ 'const char* p, const char* endp);\n';
+
+ res += '\n';
+ res += '#ifdef __cplusplus\n';
+ res += '} /* extern "C" *\/\n';
+ res += '#endif\n';
+ res += `#endif /* ${DEFINE} *\/\n`;
+
+ return res;
+ }
+}
diff --git a/llparse/src/compiler/index.ts b/llparse/src/compiler/index.ts
new file mode 100644
index 0000000..89c258a
--- /dev/null
+++ b/llparse/src/compiler/index.ts
@@ -0,0 +1,88 @@
+import * as debugAPI from 'debug';
+import * as frontend from 'llparse-frontend';
+
+import source = frontend.source;
+
+import * as cImpl from '../implementation/c';
+import { HeaderBuilder } from './header-builder';
+
+const debug = debugAPI('llparse:compiler');
+
+export interface ICompilerOptions {
+ /**
+ * Debug method name
+ *
+ * The method must have following signature:
+ *
+ * ```c
+ * void debug(llparse_t* state, const char* p, const char* endp,
+ * const char* msg);
+ * ```
+ *
+ * Where `llparse_t` is a parser state type.
+ */
+ readonly debug?: string;
+
+ /**
+ * What guard define to use in `#ifndef` in C headers.
+ *
+ * Default value: `prefix` argument
+ */
+ readonly headerGuard?: string;
+
+ /** Optional frontend configuration */
+ readonly frontend?: frontend.IFrontendLazyOptions;
+
+ /** Optional C-backend configuration */
+ readonly c?: cImpl.ICPublicOptions;
+}
+
+export interface ICompilerResult {
+ /**
+ * Textual C code
+ */
+ readonly c: string;
+
+ /**
+ * Textual C header file
+ */
+ readonly header: string;
+}
+
+export class Compiler {
+ constructor(public readonly prefix: string,
+ public readonly options: ICompilerOptions) {
+ }
+
+ public compile(root: source.node.Node,
+ properties: ReadonlyArray<source.Property>): ICompilerResult {
+ debug('Combining implementations');
+ const container = new frontend.Container();
+
+ const c = new cImpl.CCompiler(container, Object.assign({
+ debug: this.options.debug,
+ }, this.options.c));
+
+ debug('Running frontend pass');
+ const f = new frontend.Frontend(this.prefix,
+ container.build(),
+ this.options.frontend);
+ const info = f.compile(root, properties);
+
+ debug('Building header');
+ const hb = new HeaderBuilder();
+
+ const header = hb.build({
+ headerGuard: this.options.headerGuard,
+ prefix: this.prefix,
+ properties,
+ spans: info.spans,
+ });
+
+ debug('Building C');
+ return {
+ header,
+ c: c.compile(info),
+ };
+ }
+}
diff --git a/llparse/src/implementation/c/code/and.ts b/llparse/src/implementation/c/code/and.ts
new file mode 100644
index 0000000..fdd5434
--- /dev/null
+++ b/llparse/src/implementation/c/code/and.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class And extends Field<frontend.code.And> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ out.push(`${this.field(ctx)} &= ${this.ref.value};`);
+ out.push('return 0;');
+ }
+}
diff --git a/llparse/src/implementation/c/code/base.ts b/llparse/src/implementation/c/code/base.ts
new file mode 100644
index 0000000..888330d
--- /dev/null
+++ b/llparse/src/implementation/c/code/base.ts
@@ -0,0 +1,12 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+
+export abstract class Code<T extends frontend.code.Code> {
+ protected cachedDecl: string | undefined;
+
+ constructor(public readonly ref: T) {
+ }
+
+ public abstract build(ctx: Compilation, out: string[]): void;
+}
diff --git a/llparse/src/implementation/c/code/external.ts b/llparse/src/implementation/c/code/external.ts
new file mode 100644
index 0000000..494fc5a
--- /dev/null
+++ b/llparse/src/implementation/c/code/external.ts
@@ -0,0 +1,19 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Code } from './base';
+
+export abstract class External<T extends frontend.code.External>
+ extends Code<T> {
+
+ public build(ctx: Compilation, out: string[]): void {
+ out.push(`int ${this.ref.name}(`);
+ out.push(` ${ctx.prefix}_t* s, const unsigned char* p,`);
+ if (this.ref.signature === 'value') {
+ out.push(' const unsigned char* endp,');
+ out.push(' int value);');
+ } else {
+ out.push(' const unsigned char* endp);');
+ }
+ }
+}
diff --git a/llparse/src/implementation/c/code/field.ts b/llparse/src/implementation/c/code/field.ts
new file mode 100644
index 0000000..51f4439
--- /dev/null
+++ b/llparse/src/implementation/c/code/field.ts
@@ -0,0 +1,28 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Code } from './base';
+
+export abstract class Field<T extends frontend.code.Field> extends Code<T> {
+ public build(ctx: Compilation, out: string[]): void {
+ out.push(`int ${this.ref.name}(`);
+ out.push(` ${ctx.prefix}_t* ${ctx.stateArg()},`);
+ out.push(` const unsigned char* ${ctx.posArg()},`);
+ if (this.ref.signature === 'value') {
+ out.push(` const unsigned char* ${ctx.endPosArg()},`);
+ out.push(` int ${ctx.matchVar()}) {`);
+ } else {
+ out.push(` const unsigned char* ${ctx.endPosArg()}) {`);
+ }
+ const tmp: string[] = [];
+ this.doBuild(ctx, tmp);
+ ctx.indent(out, tmp, ' ');
+ out.push('}');
+ }
+
+ protected abstract doBuild(ctx: Compilation, out: string[]): void;
+
+ protected field(ctx: Compilation): string {
+ return `${ctx.stateArg()}->${this.ref.field}`;
+ }
+}
diff --git a/llparse/src/implementation/c/code/index.ts b/llparse/src/implementation/c/code/index.ts
new file mode 100644
index 0000000..0de5de5
--- /dev/null
+++ b/llparse/src/implementation/c/code/index.ts
@@ -0,0 +1,27 @@
+import * as frontend from 'llparse-frontend';
+
+import { And } from './and';
+import { External } from './external';
+import { IsEqual } from './is-equal';
+import { Load } from './load';
+import { MulAdd } from './mul-add';
+import { Or } from './or';
+import { Store } from './store';
+import { Test } from './test';
+import { Update } from './update';
+
+export * from './base';
+
+export default {
+ And,
+ IsEqual,
+ Load,
+ Match: class Match extends External<frontend.code.External> {},
+ MulAdd,
+ Or,
+ Span: class Span extends External<frontend.code.Span> {},
+ Store,
+ Test,
+ Update,
+ Value: class Value extends External<frontend.code.Value> {},
+};
diff --git a/llparse/src/implementation/c/code/is-equal.ts b/llparse/src/implementation/c/code/is-equal.ts
new file mode 100644
index 0000000..f76c2c1
--- /dev/null
+++ b/llparse/src/implementation/c/code/is-equal.ts
@@ -0,0 +1,10 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class IsEqual extends Field<frontend.code.IsEqual> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ out.push(`return ${this.field(ctx)} == ${this.ref.value};`);
+ }
+}
diff --git a/llparse/src/implementation/c/code/load.ts b/llparse/src/implementation/c/code/load.ts
new file mode 100644
index 0000000..b913f23
--- /dev/null
+++ b/llparse/src/implementation/c/code/load.ts
@@ -0,0 +1,10 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class Load extends Field<frontend.code.Load> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ out.push(`return ${this.field(ctx)};`);
+ }
+}
diff --git a/llparse/src/implementation/c/code/mul-add.ts b/llparse/src/implementation/c/code/mul-add.ts
new file mode 100644
index 0000000..fd5ce8c
--- /dev/null
+++ b/llparse/src/implementation/c/code/mul-add.ts
@@ -0,0 +1,67 @@
+import * as assert from 'assert';
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { SIGNED_LIMITS, UNSIGNED_LIMITS, SIGNED_TYPES } from '../constants';
+import { Field } from './field';
+
+export class MulAdd extends Field<frontend.code.MulAdd> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ const options = this.ref.options;
+ const ty = ctx.getFieldType(this.ref.field);
+
+ let field = this.field(ctx);
+ if (options.signed) {
+ assert(SIGNED_TYPES.has(ty), `Unexpected mulAdd type "${ty}"`);
+ const targetTy = SIGNED_TYPES.get(ty)!;
+ out.push(`${targetTy}* field = (${targetTy}*) &${field};`);
+ field = '(*field)';
+ }
+
+ const match = ctx.matchVar();
+
+ const limits = options.signed ? SIGNED_LIMITS : UNSIGNED_LIMITS;
+ assert(limits.has(ty), `Unexpected mulAdd type "${ty}"`);
+ const [ min, max ] = limits.get(ty)!;
+
+ const mulMax = `${max} / ${options.base}`;
+ const mulMin = `${min} / ${options.base}`;
+
+ out.push('/* Multiplication overflow */');
+ out.push(`if (${field} > ${mulMax}) {`);
+ out.push(' return 1;');
+ out.push('}');
+ if (options.signed) {
+ out.push(`if (${field} < ${mulMin}) {`);
+ out.push(' return 1;');
+ out.push('}');
+ }
+ out.push('');
+
+ out.push(`${field} *= ${options.base};`);
+ out.push('');
+
+ out.push('/* Addition overflow */');
+ out.push(`if (${match} >= 0) {`);
+ out.push(` if (${field} > ${max} - ${match}) {`);
+ out.push(' return 1;');
+ out.push(' }');
+ out.push('} else {');
+ out.push(` if (${field} < ${min} - ${match}) {`);
+ out.push(' return 1;');
+ out.push(' }');
+ out.push('}');
+
+ out.push(`${field} += ${match};`);
+
+ if (options.max !== undefined) {
+ out.push('');
+ out.push('/* Enforce maximum */');
+ out.push(`if (${field} > ${options.max}) {`);
+ out.push(' return 1;');
+ out.push('}');
+ }
+
+ out.push('return 0;');
+ }
+}
diff --git a/llparse/src/implementation/c/code/or.ts b/llparse/src/implementation/c/code/or.ts
new file mode 100644
index 0000000..76b16f9
--- /dev/null
+++ b/llparse/src/implementation/c/code/or.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class Or extends Field<frontend.code.Or> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ out.push(`${this.field(ctx)} |= ${this.ref.value};`);
+ out.push('return 0;');
+ }
+}
diff --git a/llparse/src/implementation/c/code/store.ts b/llparse/src/implementation/c/code/store.ts
new file mode 100644
index 0000000..a37d963
--- /dev/null
+++ b/llparse/src/implementation/c/code/store.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class Store extends Field<frontend.code.Store> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ out.push(`${this.field(ctx)} = ${ctx.matchVar()};`);
+ out.push('return 0;');
+ }
+}
diff --git a/llparse/src/implementation/c/code/test.ts b/llparse/src/implementation/c/code/test.ts
new file mode 100644
index 0000000..36126f5
--- /dev/null
+++ b/llparse/src/implementation/c/code/test.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class Test extends Field<frontend.code.Test> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ const value = this.ref.value;
+ out.push(`return (${this.field(ctx)} & ${value}) == ${value};`);
+ }
+}
diff --git a/llparse/src/implementation/c/code/update.ts b/llparse/src/implementation/c/code/update.ts
new file mode 100644
index 0000000..89efedf
--- /dev/null
+++ b/llparse/src/implementation/c/code/update.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Field } from './field';
+
+export class Update extends Field<frontend.code.Update> {
+ protected doBuild(ctx: Compilation, out: string[]): void {
+ out.push(`${this.field(ctx)} = ${this.ref.value};`);
+ out.push('return 0;');
+ }
+}
diff --git a/llparse/src/implementation/c/compilation.ts b/llparse/src/implementation/c/compilation.ts
new file mode 100644
index 0000000..4df05a6
--- /dev/null
+++ b/llparse/src/implementation/c/compilation.ts
@@ -0,0 +1,336 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+import * as frontend from 'llparse-frontend';
+
+import {
+ CONTAINER_KEY, STATE_ERROR,
+ ARG_STATE, ARG_POS, ARG_ENDPOS,
+ VAR_MATCH,
+ STATE_PREFIX, LABEL_PREFIX, BLOB_PREFIX,
+ SEQUENCE_COMPLETE, SEQUENCE_MISMATCH, SEQUENCE_PAUSE,
+} from './constants';
+import { Code } from './code';
+import { Node } from './node';
+import { Transform } from './transform';
+import { MatchSequence } from './helpers/match-sequence';
+
+// Number of hex words per line of blob declaration
+const BLOB_GROUP_SIZE = 11;
+
+type WrappedNode = frontend.IWrap<frontend.node.Node>;
+
+interface IBlob {
+ readonly alignment: number | undefined;
+ readonly buffer: Buffer;
+ readonly name: string;
+}
+
+// TODO(indutny): deduplicate
+export interface ICompilationOptions {
+ readonly debug?: string;
+}
+
+// TODO(indutny): deduplicate
+export interface ICompilationProperty {
+ readonly name: string;
+ readonly ty: string;
+}
+
+export class Compilation {
+ private readonly stateMap: Map<string, ReadonlyArray<string>> = new Map();
+ private readonly blobs: Map<Buffer, IBlob> = new Map();
+ private readonly codeMap: Map<string, Code<frontend.code.Code>> = new Map();
+ private readonly matchSequence:
+ Map<string, MatchSequence> = new Map();
+ private readonly resumptionTargets: Set<string> = new Set();
+
+ constructor(public readonly prefix: string,
+ private readonly properties: ReadonlyArray<ICompilationProperty>,
+ resumptionTargets: ReadonlySet<WrappedNode>,
+ private readonly options: ICompilationOptions) {
+ for (const node of resumptionTargets) {
+ this.resumptionTargets.add(STATE_PREFIX + node.ref.id.name);
+ }
+ }
+
+ private buildStateEnum(out: string[]): void {
+ out.push('enum llparse_state_e {');
+ out.push(` ${STATE_ERROR},`);
+ for (const stateName of this.stateMap.keys()) {
+ if (this.resumptionTargets.has(stateName)) {
+ out.push(` ${stateName},`);
+ }
+ }
+ out.push('};');
+ out.push('typedef enum llparse_state_e llparse_state_t;');
+ }
+
+ private buildBlobs(out: string[]): void {
+ if (this.blobs.size === 0) {
+ return;
+ }
+
+ for (const blob of this.blobs.values()) {
+ const buffer = blob.buffer;
+ let align = '';
+ if (blob.alignment) {
+ align = ` ALIGN(${blob.alignment})`;
+ }
+
+ if (blob.alignment) {
+ out.push('#ifdef __SSE4_2__');
+ }
+ out.push(`static const unsigned char${align} ${blob.name}[] = {`);
+
+ for (let i = 0; i < buffer.length; i += BLOB_GROUP_SIZE) {
+ const limit = Math.min(buffer.length, i + BLOB_GROUP_SIZE);
+ const hex: string[] = [];
+ for (let j = i; j < limit; j++) {
+ const value = buffer[j] as number;
+
+ const ch = String.fromCharCode(value);
+ // `'`, `\`
+ if (value === 0x27 || value === 0x5c) {
+ hex.push(`'\\${ch}'`);
+ } else if (value >= 0x20 && value <= 0x7e) {
+ hex.push(`'${ch}'`);
+ } else {
+ hex.push(`0x${value.toString(16)}`);
+ }
+ }
+ let line = ' ' + hex.join(', ');
+ if (limit !== buffer.length) {
+ line += ',';
+ }
+ out.push(line);
+ }
+
+ out.push(`};`);
+ if (blob.alignment) {
+ out.push('#endif /* __SSE4_2__ */');
+ }
+ }
+ out.push('');
+ }
+
+ private buildMatchSequence(out: string[]): void {
+ if (this.matchSequence.size === 0) {
+ return;
+ }
+
+ MatchSequence.buildGlobals(out);
+ out.push('');
+
+ for (const match of this.matchSequence.values()) {
+ match.build(this, out);
+ out.push('');
+ }
+ }
+
+ public reserveSpans(spans: ReadonlyArray<frontend.SpanField>): void {
+ for (const span of spans) {
+ for (const callback of span.callbacks) {
+ this.buildCode(this.unwrapCode(callback));
+ }
+ }
+ }
+
+ public debug(out: string[], message: string): void {
+ if (this.options.debug === undefined) {
+ return;
+ }
+
+ const args = [
+ this.stateArg(),
+ `(const char*) ${this.posArg()}`,
+ `(const char*) ${this.endPosArg()}`,
+ ];
+
+ out.push(`${this.options.debug}(${args.join(', ')},`);
+ out.push(` ${this.cstring(message)});`);
+ }
+
+ public buildGlobals(out: string[]): void {
+ if (this.options.debug !== undefined) {
+ out.push(`void ${this.options.debug}(`);
+ out.push(` ${this.prefix}_t* s, const char* p, const char* endp,`);
+ out.push(' const char* msg);');
+ }
+
+ this.buildBlobs(out);
+ this.buildMatchSequence(out);
+ this.buildStateEnum(out);
+
+ for (const code of this.codeMap.values()) {
+ out.push('');
+ code.build(this, out);
+ }
+ }
+
+ public buildResumptionStates(out: string[]): void {
+ this.stateMap.forEach((lines, name) => {
+ if (!this.resumptionTargets.has(name)) {
+ return;
+ }
+ out.push(`case ${name}:`);
+ out.push(`${LABEL_PREFIX}${name}: {`);
+ lines.forEach((line) => out.push(` ${line}`));
+ out.push(' /* UNREACHABLE */;');
+ out.push(' abort();');
+ out.push('}');
+ });
+ }
+
+ public buildInternalStates(out: string[]): void {
+ this.stateMap.forEach((lines, name) => {
+ if (this.resumptionTargets.has(name)) {
+ return;
+ }
+ out.push(`${LABEL_PREFIX}${name}: {`);
+ lines.forEach((line) => out.push(` ${line}`));
+ out.push(' /* UNREACHABLE */;');
+ out.push(' abort();');
+ out.push('}');
+ });
+ }
+
+ public addState(state: string, lines: ReadonlyArray<string>): void {
+ assert(!this.stateMap.has(state));
+ this.stateMap.set(state, lines);
+ }
+
+ public buildCode(code: Code<frontend.code.Code>): string {
+ if (this.codeMap.has(code.ref.name)) {
+ assert.strictEqual(this.codeMap.get(code.ref.name)!, code,
+ `Code name conflict for "${code.ref.name}"`);
+ } else {
+ this.codeMap.set(code.ref.name, code);
+ }
+ return code.ref.name;
+ }
+
+ public getFieldType(field: string): string {
+ for (const property of this.properties) {
+ if (property.name === field) {
+ return property.ty;
+ }
+ }
+ throw new Error(`Field "${field}" not found`);
+ }
+
+ // Helpers
+
+ public unwrapCode(code: frontend.IWrap<frontend.code.Code>)
+ : Code<frontend.code.Code> {
+ const container = code as frontend.ContainerWrap<frontend.code.Code>;
+ return container.get(CONTAINER_KEY);
+ }
+
+ public unwrapNode(node: WrappedNode): Node<frontend.node.Node> {
+ const container = node as frontend.ContainerWrap<frontend.node.Node>;
+ return container.get(CONTAINER_KEY);
+ }
+
+ public unwrapTransform(node: frontend.IWrap<frontend.transform.Transform>)
+ : Transform<frontend.transform.Transform> {
+ const container =
+ node as frontend.ContainerWrap<frontend.transform.Transform>;
+ return container.get(CONTAINER_KEY);
+ }
+
+ public indent(out: string[], lines: ReadonlyArray<string>, pad: string) {
+ for (const line of lines) {
+ out.push(`${pad}${line}`);
+ }
+ }
+
+ // MatchSequence cache
+
+ public getMatchSequence(
+ transform: frontend.IWrap<frontend.transform.Transform>, select: Buffer)
+ : string {
+ const wrap = this.unwrapTransform(transform);
+
+ let res: MatchSequence;
+ if (this.matchSequence.has(wrap.ref.name)) {
+ res = this.matchSequence.get(wrap.ref.name)!;
+ } else {
+ res = new MatchSequence(wrap);
+ this.matchSequence.set(wrap.ref.name, res);
+ }
+
+ return res.getName();
+ }
+
+ // Arguments
+
+ public stateArg(): string {
+ return ARG_STATE;
+ }
+
+ public posArg(): string {
+ return ARG_POS;
+ }
+
+ public endPosArg(): string {
+ return ARG_ENDPOS;
+ }
+
+ public matchVar(): string {
+ return VAR_MATCH;
+ }
+
+ // State fields
+
+ public indexField(): string {
+ return this.stateField('_index');
+ }
+
+ public currentField(): string {
+ return this.stateField('_current');
+ }
+
+ public errorField(): string {
+ return this.stateField('error');
+ }
+
+ public reasonField(): string {
+ return this.stateField('reason');
+ }
+
+ public errorPosField(): string {
+ return this.stateField('error_pos');
+ }
+
+ public spanPosField(index: number): string {
+ return this.stateField(`_span_pos${index}`);
+ }
+
+ public spanCbField(index: number): string {
+ return this.stateField(`_span_cb${index}`);
+ }
+
+ public stateField(name: string): string {
+ return `${this.stateArg()}->${name}`;
+ }
+
+ // Globals
+
+ public cstring(value: string): string {
+ return JSON.stringify(value);
+ }
+
+ public blob(value: Buffer, alignment?: number): string {
+ if (this.blobs.has(value)) {
+ return this.blobs.get(value)!.name;
+ }
+
+ const res = BLOB_PREFIX + this.blobs.size;
+ this.blobs.set(value, {
+ alignment,
+ buffer: value,
+ name: res,
+ });
+ return res;
+ }
+}
diff --git a/llparse/src/implementation/c/constants.ts b/llparse/src/implementation/c/constants.ts
new file mode 100644
index 0000000..bfd5be3
--- /dev/null
+++ b/llparse/src/implementation/c/constants.ts
@@ -0,0 +1,45 @@
+export const CONTAINER_KEY = 'c';
+
+export const LABEL_PREFIX = '';
+export const STATE_PREFIX = 's_n_';
+export const STATE_ERROR = 's_error';
+
+export const BLOB_PREFIX = 'llparse_blob';
+
+export const ARG_STATE = 'state';
+export const ARG_POS = 'p';
+export const ARG_ENDPOS = 'endp';
+
+export const VAR_MATCH = 'match';
+
+// MatchSequence
+
+export const SEQUENCE_COMPLETE = 'kMatchComplete';
+export const SEQUENCE_MISMATCH = 'kMatchMismatch';
+export const SEQUENCE_PAUSE = 'kMatchPause';
+
+export const SIGNED_LIMITS: Map<string, [ string, string ]> = new Map();
+SIGNED_LIMITS.set('i8', [ '-0x80', '0x7f' ]);
+SIGNED_LIMITS.set('i16', [ '-0x8000', '0x7fff' ]);
+SIGNED_LIMITS.set('i32', [ '(-0x7fffffff - 1)', '0x7fffffff' ]);
+SIGNED_LIMITS.set('i64', [ '(-0x7fffffffffffffffLL - 1)',
+ '0x7fffffffffffffffLL' ]);
+
+export const UNSIGNED_LIMITS: Map<string, [ string, string ]> = new Map();
+UNSIGNED_LIMITS.set('i8', [ '0', '0xff' ]);
+UNSIGNED_LIMITS.set('i8', [ '0', '0xff' ]);
+UNSIGNED_LIMITS.set('i16', [ '0', '0xffff' ]);
+UNSIGNED_LIMITS.set('i32', [ '0', '0xffffffff' ]);
+UNSIGNED_LIMITS.set('i64', [ '0ULL', '0xffffffffffffffffULL' ]);
+
+export const UNSIGNED_TYPES: Map<string, string> = new Map();
+UNSIGNED_TYPES.set('i8', 'uint8_t');
+UNSIGNED_TYPES.set('i16', 'uint16_t');
+UNSIGNED_TYPES.set('i32', 'uint32_t');
+UNSIGNED_TYPES.set('i64', 'uint64_t');
+
+export const SIGNED_TYPES: Map<string, string> = new Map();
+SIGNED_TYPES.set('i8', 'int8_t');
+SIGNED_TYPES.set('i16', 'int16_t');
+SIGNED_TYPES.set('i32', 'int32_t');
+SIGNED_TYPES.set('i64', 'int64_t');
diff --git a/llparse/src/implementation/c/helpers/match-sequence.ts b/llparse/src/implementation/c/helpers/match-sequence.ts
new file mode 100644
index 0000000..278f4b5
--- /dev/null
+++ b/llparse/src/implementation/c/helpers/match-sequence.ts
@@ -0,0 +1,75 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+import * as frontend from 'llparse-frontend';
+
+import {
+ SEQUENCE_COMPLETE, SEQUENCE_MISMATCH, SEQUENCE_PAUSE,
+} from '../constants';
+import { Transform } from '../transform';
+import { Compilation } from '../compilation';
+
+type TransformWrap = Transform<frontend.transform.Transform>;
+
+export class MatchSequence {
+ constructor(private readonly transform: TransformWrap) {
+ }
+
+ public static buildGlobals(out: string[]): void {
+ out.push('enum llparse_match_status_e {');
+ out.push(` ${SEQUENCE_COMPLETE},`);
+ out.push(` ${SEQUENCE_PAUSE},`);
+ out.push(` ${SEQUENCE_MISMATCH}`);
+ out.push('};');
+ out.push('typedef enum llparse_match_status_e llparse_match_status_t;');
+ out.push('');
+ out.push('struct llparse_match_s {');
+ out.push(' llparse_match_status_t status;');
+ out.push(' const unsigned char* current;');
+ out.push('};');
+ out.push('typedef struct llparse_match_s llparse_match_t;');
+ }
+
+ public getName(): string {
+ return `llparse__match_sequence_${this.transform.ref.name}`;
+ }
+
+ public build(ctx: Compilation, out: string[]): void {
+ out.push(`static llparse_match_t ${this.getName()}(`);
+ out.push(` ${ctx.prefix}_t* s, const unsigned char* p,`);
+ out.push(' const unsigned char* endp,');
+ out.push(' const unsigned char* seq, uint32_t seq_len) {');
+
+ // Vars
+ out.push(' uint32_t index;');
+ out.push(' llparse_match_t res;');
+ out.push('');
+
+ // Body
+ out.push(' index = s->_index;');
+ out.push(' for (; p != endp; p++) {');
+ out.push(' unsigned char current;');
+ out.push('');
+ out.push(` current = ${this.transform.build(ctx, '*p')};`);
+ out.push(' if (current == seq[index]) {');
+ out.push(' if (++index == seq_len) {');
+ out.push(` res.status = ${SEQUENCE_COMPLETE};`);
+ out.push(' goto reset;');
+ out.push(' }');
+ out.push(' } else {');
+ out.push(` res.status = ${SEQUENCE_MISMATCH};`);
+ out.push(' goto reset;');
+ out.push(' }');
+ out.push(' }');
+
+ out.push(' s->_index = index;');
+ out.push(` res.status = ${SEQUENCE_PAUSE};`);
+ out.push(' res.current = p;');
+ out.push(' return res;');
+
+ out.push('reset:');
+ out.push(' s->_index = 0;');
+ out.push(' res.current = p;');
+ out.push(' return res;');
+ out.push('}');
+ }
+}
diff --git a/llparse/src/implementation/c/index.ts b/llparse/src/implementation/c/index.ts
new file mode 100644
index 0000000..ae94d34
--- /dev/null
+++ b/llparse/src/implementation/c/index.ts
@@ -0,0 +1,199 @@
+import * as frontend from 'llparse-frontend';
+
+import {
+ ARG_STATE, ARG_POS, ARG_ENDPOS,
+ STATE_ERROR,
+ VAR_MATCH,
+ CONTAINER_KEY,
+} from './constants';
+import { Compilation } from './compilation';
+import code from './code';
+import node from './node';
+import { Node } from './node';
+import transform from './transform';
+
+export interface ICCompilerOptions {
+ readonly debug?: string;
+ readonly header?: string;
+}
+
+export interface ICPublicOptions {
+ readonly header?: string;
+}
+
+export class CCompiler {
+ constructor(container: frontend.Container,
+ public readonly options: ICCompilerOptions) {
+ container.add(CONTAINER_KEY, { code, node, transform });
+ }
+
+ public compile(info: frontend.IFrontendResult): string {
+ const compilation = new Compilation(info.prefix, info.properties,
+ info.resumptionTargets, this.options);
+ const out: string[] = [];
+
+ out.push('#include <stdlib.h>');
+ out.push('#include <stdint.h>');
+ out.push('#include <string.h>');
+ out.push('');
+
+ // NOTE: Inspired by https://github.com/h2o/picohttpparser
+ // TODO(indutny): Windows support for SSE4.2.
+ // See: https://github.com/nodejs/llparse/pull/24#discussion_r299789676
+ // (There is no `__SSE4_2__` define for MSVC)
+ out.push('#ifdef __SSE4_2__');
+ out.push(' #ifdef _MSC_VER');
+ out.push(' #include <nmmintrin.h>');
+ out.push(' #else /* !_MSC_VER */');
+ out.push(' #include <x86intrin.h>');
+ out.push(' #endif /* _MSC_VER */');
+ out.push('#endif /* __SSE4_2__ */');
+ out.push('');
+
+ out.push('#ifdef _MSC_VER');
+ out.push(' #define ALIGN(n) _declspec(align(n))');
+ out.push('#else /* !_MSC_VER */');
+ out.push(' #define ALIGN(n) __attribute__((aligned(n)))');
+ out.push('#endif /* _MSC_VER */');
+
+ out.push('');
+ out.push(`#include "${this.options.header || info.prefix}.h"`);
+ out.push(``);
+ out.push(`typedef int (*${info.prefix}__span_cb)(`);
+ out.push(` ${info.prefix}_t*, const char*, const char*);`);
+ out.push('');
+
+ // Queue span callbacks to be built before `executeSpans()` code gets called
+ // below.
+ compilation.reserveSpans(info.spans);
+
+ const root = info.root as frontend.ContainerWrap<frontend.node.Node>;
+ const rootState = root.get<Node<frontend.node.Node>>(CONTAINER_KEY)
+ .build(compilation);
+
+ compilation.buildGlobals(out);
+ out.push('');
+
+ out.push(`int ${info.prefix}_init(${info.prefix}_t* ${ARG_STATE}) {`);
+ out.push(` memset(${ARG_STATE}, 0, sizeof(*${ARG_STATE}));`);
+ out.push(` ${ARG_STATE}->_current = (void*) (intptr_t) ${rootState};`);
+ out.push(' return 0;');
+ out.push('}');
+ out.push('');
+
+ out.push(`static llparse_state_t ${info.prefix}__run(`);
+ out.push(` ${info.prefix}_t* ${ARG_STATE},`);
+ out.push(` const unsigned char* ${ARG_POS},`);
+ out.push(` const unsigned char* ${ARG_ENDPOS}) {`);
+ out.push(` int ${VAR_MATCH};`);
+ out.push(` switch ((llparse_state_t) (intptr_t) ` +
+ `${compilation.currentField()}) {`);
+
+ let tmp: string[] = [];
+ compilation.buildResumptionStates(tmp);
+ compilation.indent(out, tmp, ' ');
+
+ out.push(' default:');
+ out.push(' /* UNREACHABLE */');
+ out.push(' abort();');
+ out.push(' }');
+
+ tmp = [];
+ compilation.buildInternalStates(tmp);
+ compilation.indent(out, tmp, ' ');
+
+ out.push('}');
+ out.push('');
+
+
+ out.push(`int ${info.prefix}_execute(${info.prefix}_t* ${ARG_STATE}, ` +
+ `const char* ${ARG_POS}, const char* ${ARG_ENDPOS}) {`);
+ out.push(' llparse_state_t next;');
+ out.push('');
+
+ out.push(' /* check lingering errors */');
+ out.push(` if (${compilation.errorField()} != 0) {`);
+ out.push(` return ${compilation.errorField()};`);
+ out.push(' }');
+ out.push('');
+
+ tmp = [];
+ this.restartSpans(compilation, info, tmp);
+ compilation.indent(out, tmp, ' ');
+
+ const args = [
+ compilation.stateArg(),
+ `(const unsigned char*) ${compilation.posArg()}`,
+ `(const unsigned char*) ${compilation.endPosArg()}`,
+ ];
+ out.push(` next = ${info.prefix}__run(${args.join(', ')});`);
+ out.push(` if (next == ${STATE_ERROR}) {`);
+ out.push(` return ${compilation.errorField()};`);
+ out.push(' }');
+ out.push(` ${compilation.currentField()} = (void*) (intptr_t) next;`);
+ out.push('');
+
+ tmp = [];
+ this.executeSpans(compilation, info, tmp);
+ compilation.indent(out, tmp, ' ');
+
+ out.push(' return 0;');
+ out.push('}');
+
+ return out.join('\n');
+ }
+
+ private restartSpans(ctx: Compilation, info: frontend.IFrontendResult,
+ out: string[]): void {
+ if (info.spans.length === 0) {
+ return;
+ }
+
+ out.push('/* restart spans */');
+ for (const span of info.spans) {
+ const posField = ctx.spanPosField(span.index);
+
+ out.push(`if (${posField} != NULL) {`);
+ out.push(` ${posField} = (void*) ${ctx.posArg()};`);
+ out.push('}');
+ }
+ out.push('');
+ }
+
+ private executeSpans(ctx: Compilation, info: frontend.IFrontendResult,
+ out: string[]): void {
+ if (info.spans.length === 0) {
+ return;
+ }
+
+ out.push('/* execute spans */');
+ for (const span of info.spans) {
+ const posField = ctx.spanPosField(span.index);
+ let callback: string;
+ if (span.callbacks.length === 1) {
+ callback = ctx.buildCode(ctx.unwrapCode(span.callbacks[0]));
+ } else {
+ callback = `(${info.prefix}__span_cb) ` + ctx.spanCbField(span.index);
+ callback = `(${callback})`;
+ }
+
+ const args = [
+ ctx.stateArg(), posField, `(const char*) ${ctx.endPosArg()}`,
+ ];
+
+ out.push(`if (${posField} != NULL) {`);
+ out.push(' int error;');
+ out.push('');
+ out.push(` error = ${callback}(${args.join(', ')});`);
+
+ // TODO(indutny): de-duplicate this here and in SpanEnd
+ out.push(' if (error != 0) {');
+ out.push(` ${ctx.errorField()} = error;`);
+ out.push(` ${ctx.errorPosField()} = ${ctx.endPosArg()};`);
+ out.push(' return error;');
+ out.push(' }');
+ out.push('}');
+ }
+ out.push('');
+ }
+}
diff --git a/llparse/src/implementation/c/node/base.ts b/llparse/src/implementation/c/node/base.ts
new file mode 100644
index 0000000..51f90bb
--- /dev/null
+++ b/llparse/src/implementation/c/node/base.ts
@@ -0,0 +1,77 @@
+import * as assert from 'assert';
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import {
+ STATE_PREFIX, LABEL_PREFIX,
+} from '../constants';
+
+export interface INodeEdge {
+ readonly node: frontend.IWrap<frontend.node.Node>;
+ readonly noAdvance: boolean;
+ readonly value?: number;
+}
+
+export abstract class Node<T extends frontend.node.Node> {
+ protected cachedDecl: string | undefined;
+ protected privCompilation: Compilation | undefined;
+
+ constructor(public readonly ref: T) {
+ }
+
+ public build(compilation: Compilation): string {
+ if (this.cachedDecl !== undefined) {
+ return this.cachedDecl;
+ }
+
+ const res = STATE_PREFIX + this.ref.id.name;
+ this.cachedDecl = res;
+
+ this.privCompilation = compilation;
+
+ const out: string[] = [];
+ compilation.debug(out,
+ `Entering node "${this.ref.id.originalName}" ("${this.ref.id.name}")`);
+ this.doBuild(out);
+
+ compilation.addState(res, out);
+
+ return res;
+ }
+
+ protected get compilation(): Compilation {
+ assert(this.privCompilation !== undefined);
+ return this.privCompilation!;
+ }
+
+ protected prologue(out: string[]): void {
+ const ctx = this.compilation;
+
+ out.push(`if (${ctx.posArg()} == ${ctx.endPosArg()}) {`);
+
+ const tmp: string[] = [];
+ this.pause(tmp);
+ this.compilation.indent(out, tmp, ' ');
+
+ out.push('}');
+ }
+
+ protected pause(out: string[]): void {
+ out.push(`return ${this.cachedDecl};`);
+ }
+
+ protected tailTo(out: string[], edge: INodeEdge): void {
+ const ctx = this.compilation;
+ const target = ctx.unwrapNode(edge.node).build(ctx);
+
+ if (!edge.noAdvance) {
+ out.push(`${ctx.posArg()}++;`);
+ }
+ if (edge.value !== undefined) {
+ out.push(`${ctx.matchVar()} = ${edge.value};`);
+ }
+ out.push(`goto ${LABEL_PREFIX}${target};`);
+ }
+
+ protected abstract doBuild(out: string[]): void;
+}
diff --git a/llparse/src/implementation/c/node/consume.ts b/llparse/src/implementation/c/node/consume.ts
new file mode 100644
index 0000000..658a00e
--- /dev/null
+++ b/llparse/src/implementation/c/node/consume.ts
@@ -0,0 +1,48 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Node } from './base';
+
+export class Consume extends Node<frontend.node.Consume> {
+ public doBuild(out: string[]): void {
+ const ctx = this.compilation;
+
+ const index = ctx.stateField(this.ref.field);
+ const ty = ctx.getFieldType(this.ref.field);
+
+ let fieldTy: string;
+ if (ty === 'i64') {
+ fieldTy = 'uint64_t';
+ } else if (ty === 'i32') {
+ fieldTy = 'uint32_t';
+ } else if (ty === 'i16') {
+ fieldTy = 'uint16_t';
+ } else if (ty === 'i8') {
+ fieldTy = 'uint8_t';
+ } else {
+ throw new Error(
+ `Unsupported type ${ty} of field ${this.ref.field} for consume node`);
+ }
+
+ out.push('size_t avail;');
+ out.push(`${fieldTy} need;`);
+
+ out.push('');
+ out.push(`avail = ${ctx.endPosArg()} - ${ctx.posArg()};`);
+ out.push(`need = ${index};`);
+
+ // Note: `avail` or `need` are going to coerced to the largest
+ // datatype needed to hold either of the values.
+ out.push('if (avail >= need) {');
+ out.push(` p += need;`);
+ out.push(` ${index} = 0;`);
+ const tmp: string[] = [];
+ this.tailTo(tmp, this.ref.otherwise!);
+ ctx.indent(out, tmp, ' ');
+ out.push('}');
+ out.push('');
+
+ out.push(`${index} -= avail;`);
+ this.pause(out);
+ }
+}
diff --git a/llparse/src/implementation/c/node/empty.ts b/llparse/src/implementation/c/node/empty.ts
new file mode 100644
index 0000000..e28ecb5
--- /dev/null
+++ b/llparse/src/implementation/c/node/empty.ts
@@ -0,0 +1,16 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Node } from './base';
+
+export class Empty extends Node<frontend.node.Empty> {
+ public doBuild(out: string[]): void {
+ const otherwise = this.ref.otherwise!;
+
+ if (!otherwise.noAdvance) {
+ this.prologue(out);
+ }
+
+ this.tailTo(out, otherwise);
+ }
+}
diff --git a/llparse/src/implementation/c/node/error.ts b/llparse/src/implementation/c/node/error.ts
new file mode 100644
index 0000000..29dce63
--- /dev/null
+++ b/llparse/src/implementation/c/node/error.ts
@@ -0,0 +1,33 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { STATE_ERROR } from '../constants';
+import { Node } from './base';
+
+class ErrorNode<T extends frontend.node.Error> extends Node<T> {
+ protected storeError(out: string[]): void {
+ const ctx = this.compilation;
+
+ let hexCode: string;
+ if (this.ref.code < 0) {
+ hexCode = `-0x` + this.ref.code.toString(16);
+ } else {
+ hexCode = '0x' + this.ref.code.toString(16);
+ }
+
+ out.push(`${ctx.errorField()} = ${hexCode};`);
+ out.push(`${ctx.reasonField()} = ${ctx.cstring(this.ref.reason)};`);
+ out.push(`${ctx.errorPosField()} = (const char*) ${ctx.posArg()};`);
+ }
+
+ public doBuild(out: string[]): void {
+ this.storeError(out);
+
+ // Non-recoverable state
+ out.push(`${this.compilation.currentField()} = ` +
+ `(void*) (intptr_t) ${STATE_ERROR};`);
+ out.push(`return ${STATE_ERROR};`);
+ }
+}
+
+export { ErrorNode as Error };
diff --git a/llparse/src/implementation/c/node/index.ts b/llparse/src/implementation/c/node/index.ts
new file mode 100644
index 0000000..ba751d9
--- /dev/null
+++ b/llparse/src/implementation/c/node/index.ts
@@ -0,0 +1,27 @@
+import * as frontend from 'llparse-frontend';
+
+import { Consume } from './consume';
+import { Empty } from './empty';
+import { Error as ErrorNode } from './error';
+import { Invoke } from './invoke';
+import { Pause } from './pause';
+import { Sequence } from './sequence';
+import { Single } from './single';
+import { SpanEnd } from './span-end';
+import { SpanStart } from './span-start';
+import { TableLookup } from './table-lookup';
+
+export { Node } from './base';
+
+export default {
+ Consume,
+ Empty,
+ Error: class Error extends ErrorNode<frontend.node.Error> {},
+ Invoke,
+ Pause,
+ Sequence,
+ Single,
+ SpanEnd,
+ SpanStart,
+ TableLookup,
+};
diff --git a/llparse/src/implementation/c/node/invoke.ts b/llparse/src/implementation/c/node/invoke.ts
new file mode 100644
index 0000000..ee917e9
--- /dev/null
+++ b/llparse/src/implementation/c/node/invoke.ts
@@ -0,0 +1,44 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Node } from './base';
+
+export class Invoke extends Node<frontend.node.Invoke> {
+ public doBuild(out: string[]): void {
+ const ctx = this.compilation;
+
+ const code = ctx.unwrapCode(this.ref.code);
+ const codeDecl = ctx.buildCode(code);
+
+ const args: string[] = [
+ ctx.stateArg(),
+ ctx.posArg(),
+ ctx.endPosArg(),
+ ];
+
+ const signature = code.ref.signature;
+ if (signature === 'value') {
+ args.push(ctx.matchVar());
+ }
+
+ out.push(`switch (${codeDecl}(${args.join(', ')})) {`);
+ let tmp: string[];
+
+ for (const edge of this.ref.edges) {
+ out.push(` case ${edge.code}:`);
+ tmp = [];
+ this.tailTo(tmp, {
+ noAdvance: true,
+ node: edge.node,
+ value: undefined,
+ });
+ ctx.indent(out, tmp, ' ');
+ }
+
+ out.push(' default:');
+ tmp = [];
+ this.tailTo(tmp, this.ref.otherwise!);
+ ctx.indent(out, tmp, ' ');
+ out.push('}');
+ }
+}
diff --git a/llparse/src/implementation/c/node/pause.ts b/llparse/src/implementation/c/node/pause.ts
new file mode 100644
index 0000000..c239b46
--- /dev/null
+++ b/llparse/src/implementation/c/node/pause.ts
@@ -0,0 +1,19 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { STATE_ERROR } from '../constants';
+import { Error as ErrorNode } from './error';
+
+export class Pause extends ErrorNode<frontend.node.Pause> {
+ public doBuild(out: string[]): void {
+ const ctx = this.compilation;
+
+ this.storeError(out);
+
+ // Recoverable state
+ const otherwise = ctx.unwrapNode(this.ref.otherwise!.node).build(ctx);
+ out.push(`${ctx.currentField()} = ` +
+ `(void*) (intptr_t) ${otherwise};`);
+ out.push(`return ${STATE_ERROR};`);
+ }
+}
diff --git a/llparse/src/implementation/c/node/sequence.ts b/llparse/src/implementation/c/node/sequence.ts
new file mode 100644
index 0000000..73d8816
--- /dev/null
+++ b/llparse/src/implementation/c/node/sequence.ts
@@ -0,0 +1,55 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import {
+ SEQUENCE_COMPLETE, SEQUENCE_MISMATCH, SEQUENCE_PAUSE,
+} from '../constants';
+import { Node } from './base';
+
+export class Sequence extends Node<frontend.node.Sequence> {
+ public doBuild(out: string[]): void {
+ const ctx = this.compilation;
+
+ out.push('llparse_match_t match_seq;');
+ out.push('');
+
+ this.prologue(out);
+
+ const matchSequence = ctx.getMatchSequence(this.ref.transform!,
+ this.ref.select);
+
+ out.push(`match_seq = ${matchSequence}(${ctx.stateArg()}, ` +
+ `${ctx.posArg()}, ` +
+ `${ctx.endPosArg()}, ${ctx.blob(this.ref.select)}, ` +
+ `${this.ref.select.length});`);
+ out.push('p = match_seq.current;');
+
+ let tmp: string[];
+
+ out.push('switch (match_seq.status) {');
+
+ out.push(` case ${SEQUENCE_COMPLETE}: {`);
+ tmp = [];
+ this.tailTo(tmp, {
+ noAdvance: false,
+ node: this.ref.edge!.node,
+ value: this.ref.edge!.value,
+ });
+ ctx.indent(out, tmp, ' ');
+ out.push(' }');
+
+ out.push(` case ${SEQUENCE_PAUSE}: {`);
+ tmp = [];
+ this.pause(tmp);
+ ctx.indent(out, tmp, ' ');
+ out.push(' }');
+
+ out.push(` case ${SEQUENCE_MISMATCH}: {`);
+ tmp = [];
+ this.tailTo(tmp, this.ref.otherwise!);
+ ctx.indent(out, tmp, ' ');
+ out.push(' }');
+
+ out.push('}');
+ }
+}
diff --git a/llparse/src/implementation/c/node/single.ts b/llparse/src/implementation/c/node/single.ts
new file mode 100644
index 0000000..b9c8811
--- /dev/null
+++ b/llparse/src/implementation/c/node/single.ts
@@ -0,0 +1,47 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Node } from './base';
+
+export class Single extends Node<frontend.node.Single> {
+ public doBuild(out: string[]): void {
+ const ctx = this.compilation;
+
+ const otherwise = this.ref.otherwise!;
+
+ this.prologue(out);
+
+ const transform = ctx.unwrapTransform(this.ref.transform!);
+ const current = transform.build(ctx, `*${ctx.posArg()}`);
+
+ out.push(`switch (${current}) {`)
+ this.ref.edges.forEach((edge) => {
+ let ch: string;
+
+ // Non-printable ASCII, or single-quote, or forward slash
+ if (edge.key < 0x20 || edge.key > 0x7e || edge.key === 0x27 ||
+ edge.key === 0x5c) {
+ ch = edge.key.toString();
+ } else {
+ ch = `'${String.fromCharCode(edge.key)}'`;
+ }
+ out.push(` case ${ch}: {`);
+
+ const tmp: string[] = [];
+ this.tailTo(tmp, edge);
+ ctx.indent(out, tmp, ' ');
+
+ out.push(' }');
+ });
+
+ out.push(` default: {`);
+
+ const tmp: string[] = [];
+ this.tailTo(tmp, otherwise);
+ ctx.indent(out, tmp, ' ');
+
+ out.push(' }');
+
+ out.push(`}`);
+ }
+}
diff --git a/llparse/src/implementation/c/node/span-end.ts b/llparse/src/implementation/c/node/span-end.ts
new file mode 100644
index 0000000..09f97e5
--- /dev/null
+++ b/llparse/src/implementation/c/node/span-end.ts
@@ -0,0 +1,56 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { STATE_ERROR } from '../constants';
+import { Node } from './base';
+
+export class SpanEnd extends Node<frontend.node.SpanEnd> {
+ public doBuild(out: string[]): void {
+ out.push('const unsigned char* start;');
+ out.push('int err;');
+ out.push('');
+
+ const ctx = this.compilation;
+ const field = this.ref.field;
+ const posField = ctx.spanPosField(field.index);
+
+ // Load start position
+ out.push(`start = ${posField};`);
+
+ // ...and reset
+ out.push(`${posField} = NULL;`);
+
+ // Invoke callback
+ const callback = ctx.buildCode(ctx.unwrapCode(this.ref.callback));
+ out.push(`err = ${callback}(${ctx.stateArg()}, start, ${ctx.posArg()});`);
+
+ out.push('if (err != 0) {');
+ const tmp: string[] = [];
+ this.buildError(tmp, 'err');
+ ctx.indent(out, tmp, ' ');
+ out.push('}');
+
+ const otherwise = this.ref.otherwise!;
+ this.tailTo(out, otherwise);
+ }
+
+ private buildError(out: string[], code: string) {
+ const ctx = this.compilation;
+
+ out.push(`${ctx.errorField()} = ${code};`);
+
+ const otherwise = this.ref.otherwise!;
+ let resumePos = ctx.posArg();
+ if (!otherwise.noAdvance) {
+ resumePos = `(${resumePos} + 1)`;
+ }
+
+ out.push(`${ctx.errorPosField()} = (const char*) ${resumePos};`);
+
+ const resumptionTarget = ctx.unwrapNode(otherwise.node).build(ctx);
+ out.push(`${ctx.currentField()} = ` +
+ `(void*) (intptr_t) ${resumptionTarget};`);
+
+ out.push(`return ${STATE_ERROR};`);
+ }
+}
diff --git a/llparse/src/implementation/c/node/span-start.ts b/llparse/src/implementation/c/node/span-start.ts
new file mode 100644
index 0000000..445da67
--- /dev/null
+++ b/llparse/src/implementation/c/node/span-start.ts
@@ -0,0 +1,26 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Node } from './base';
+
+export class SpanStart extends Node<frontend.node.SpanStart> {
+ public doBuild(out: string[]): void {
+ // Prevent spurious empty spans
+ this.prologue(out);
+
+ const ctx = this.compilation;
+ const field = this.ref.field;
+
+ const posField = ctx.spanPosField(field.index);
+ out.push(`${posField} = (void*) ${ctx.posArg()};`);
+
+ if (field.callbacks.length > 1) {
+ const cbField = ctx.spanCbField(field.index);
+ const callback = ctx.unwrapCode(this.ref.callback);
+ out.push(`${cbField} = ${ctx.buildCode(callback)};`);
+ }
+
+ const otherwise = this.ref.otherwise!;
+ this.tailTo(out, otherwise);
+ }
+}
diff --git a/llparse/src/implementation/c/node/table-lookup.ts b/llparse/src/implementation/c/node/table-lookup.ts
new file mode 100644
index 0000000..6a400a3
--- /dev/null
+++ b/llparse/src/implementation/c/node/table-lookup.ts
@@ -0,0 +1,196 @@
+import * as assert from 'assert';
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Node } from './base';
+
+const MAX_CHAR = 0xff;
+const TABLE_GROUP = 16;
+
+// _mm_cmpestri takes 8 ranges
+const SSE_RANGES_LEN = 16;
+// _mm_cmpestri takes 128bit input
+const SSE_RANGES_PAD = 16;
+const MAX_SSE_CALLS = 2;
+const SSE_ALIGNMENT = 16;
+
+interface ITable {
+ readonly name: string;
+ readonly declaration: ReadonlyArray<string>;
+}
+
+export class TableLookup extends Node<frontend.node.TableLookup> {
+ public doBuild(out: string[]): void {
+ const ctx = this.compilation;
+
+ const table = this.buildTable();
+ for (const line of table.declaration) {
+ out.push(line);
+ }
+
+ this.prologue(out);
+
+ const transform = ctx.unwrapTransform(this.ref.transform!);
+
+ // Try to vectorize nodes matching characters and looping to themselves
+ // NOTE: `switch` below triggers when there is not enough characters in the
+ // stream for vectorized processing.
+ this.buildSSE(out);
+
+ const current = transform.build(ctx, `*${ctx.posArg()}`);
+ out.push(`switch (${table.name}[(uint8_t) ${current}]) {`);
+
+ for (const [ index, edge ] of this.ref.edges.entries()) {
+ out.push(` case ${index + 1}: {`);
+
+ const tmp: string[] = [];
+ const edge = this.ref.edges[index];
+ this.tailTo(tmp, {
+ noAdvance: edge.noAdvance,
+ node: edge.node,
+ value: undefined,
+ });
+ ctx.indent(out, tmp, ' ');
+
+ out.push(' }');
+ }
+
+ out.push(` default: {`);
+
+ const tmp: string[] = [];
+ this.tailTo(tmp, this.ref.otherwise!);
+ ctx.indent(out, tmp, ' ');
+
+ out.push(' }');
+ out.push('}');
+ }
+
+ private buildSSE(out: string[]): boolean {
+ const ctx = this.compilation;
+
+ // Transformation is not supported atm
+ if (this.ref.transform && this.ref.transform.ref.name !== 'id') {
+ return false;
+ }
+
+ if (this.ref.edges.length !== 1) {
+ return false;
+ }
+
+ const edge = this.ref.edges[0];
+ if (edge.node.ref !== this.ref) {
+ return false;
+ }
+
+ // NOTE: keys are sorted
+ let ranges: number[] = [];
+ let first: number | undefined;
+ let last: number | undefined;
+ for (const key of edge.keys) {
+ if (first === undefined) {
+ first = key;
+ }
+ if (last === undefined) {
+ last = key;
+ }
+
+ if (key - last > 1) {
+ ranges.push(first, last);
+ first = key;
+ }
+ last = key;
+ }
+ if (first !== undefined && last !== undefined) {
+ ranges.push(first, last);
+ }
+
+ if (ranges.length === 0) {
+ return false;
+ }
+
+ // Way too many calls would be required
+ if (ranges.length > MAX_SSE_CALLS * SSE_RANGES_LEN) {
+ return false;
+ }
+
+ out.push('#ifdef __SSE4_2__');
+ out.push(`if (${ctx.endPosArg()} - ${ctx.posArg()} >= 16) {`);
+ out.push(' __m128i ranges;');
+ out.push(' __m128i input;');
+ out.push(' int avail;');
+ out.push(' int match_len;');
+ out.push('');
+ out.push(' /* Load input */');
+ out.push(` input = _mm_loadu_si128((__m128i const*) ${ctx.posArg()});`);
+ for (let off = 0; off < ranges.length; off += SSE_RANGES_LEN) {
+ const subRanges = ranges.slice(off, off + SSE_RANGES_LEN);
+
+ let paddedRanges = subRanges.slice();
+ while (paddedRanges.length < SSE_RANGES_PAD) {
+ paddedRanges.push(0);
+ }
+
+ const blob = ctx.blob(Buffer.from(paddedRanges), SSE_ALIGNMENT);
+ out.push(` ranges = _mm_loadu_si128((__m128i const*) ${blob});`);
+ out.push('');
+
+ out.push(' /* Find first character that does not match `ranges` */');
+ out.push(` match_len = _mm_cmpestri(ranges, ${subRanges.length},`);
+ out.push(' input, 16,');
+ out.push(' _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES |');
+ out.push(' _SIDD_NEGATIVE_POLARITY);');
+ out.push('');
+ out.push(' if (match_len != 0) {');
+ out.push(` ${ctx.posArg()} += match_len;`);
+
+ const tmp: string[] = [];
+ assert.strictEqual(edge.noAdvance, false);
+ this.tailTo(tmp, {
+ noAdvance: true,
+ node: edge.node,
+ });
+ ctx.indent(out, tmp, ' ');
+
+ out.push(' }');
+ }
+
+ {
+ const tmp: string[] = [];
+ this.tailTo(tmp, this.ref.otherwise!);
+ ctx.indent(out, tmp, ' ');
+ }
+ out.push('}');
+
+ out.push('#endif /* __SSE4_2__ */');
+
+ return true;
+ }
+
+ private buildTable(): ITable {
+ const table: number[] = new Array(MAX_CHAR + 1).fill(0);
+
+ for (const [ index, edge ] of this.ref.edges.entries()) {
+ edge.keys.forEach((key) => {
+ assert.strictEqual(table[key], 0);
+ table[key] = index + 1;
+ });
+ }
+
+ const lines = [
+ 'static uint8_t lookup_table[] = {',
+ ];
+ for (let i = 0; i < table.length; i += TABLE_GROUP) {
+ let line = ` ${table.slice(i, i + TABLE_GROUP).join(', ')}`;
+ if (i + TABLE_GROUP < table.length) {
+ line += ',';
+ }
+ lines.push(line);
+ }
+ lines.push('};');
+
+ return {
+ name: 'lookup_table',
+ declaration: lines,
+ };
+ }
+}
diff --git a/llparse/src/implementation/c/transform/base.ts b/llparse/src/implementation/c/transform/base.ts
new file mode 100644
index 0000000..82028d5
--- /dev/null
+++ b/llparse/src/implementation/c/transform/base.ts
@@ -0,0 +1,10 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+
+export abstract class Transform<T extends frontend.transform.Transform> {
+ constructor(public readonly ref: T) {
+ }
+
+ public abstract build(ctx: Compilation, value: string): string;
+}
diff --git a/llparse/src/implementation/c/transform/id.ts b/llparse/src/implementation/c/transform/id.ts
new file mode 100644
index 0000000..6c6105f
--- /dev/null
+++ b/llparse/src/implementation/c/transform/id.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Transform } from './base';
+
+export class ID extends Transform<frontend.transform.ID> {
+ public build(ctx: Compilation, value: string): string {
+ // Identity transformation
+ return value;
+ }
+}
diff --git a/llparse/src/implementation/c/transform/index.ts b/llparse/src/implementation/c/transform/index.ts
new file mode 100644
index 0000000..c13ba50
--- /dev/null
+++ b/llparse/src/implementation/c/transform/index.ts
@@ -0,0 +1,11 @@
+import { ID } from './id';
+import { ToLower } from './to-lower';
+import { ToLowerUnsafe } from './to-lower-unsafe';
+
+export { Transform } from './base';
+
+export default {
+ ID,
+ ToLower,
+ ToLowerUnsafe,
+};
diff --git a/llparse/src/implementation/c/transform/to-lower-unsafe.ts b/llparse/src/implementation/c/transform/to-lower-unsafe.ts
new file mode 100644
index 0000000..27f608c
--- /dev/null
+++ b/llparse/src/implementation/c/transform/to-lower-unsafe.ts
@@ -0,0 +1,10 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform<frontend.transform.ToLowerUnsafe> {
+ public build(ctx: Compilation, value: string): string {
+ return `((${value}) | 0x20)`;
+ }
+}
diff --git a/llparse/src/implementation/c/transform/to-lower.ts b/llparse/src/implementation/c/transform/to-lower.ts
new file mode 100644
index 0000000..f639ef1
--- /dev/null
+++ b/llparse/src/implementation/c/transform/to-lower.ts
@@ -0,0 +1,11 @@
+import * as frontend from 'llparse-frontend';
+
+import { Compilation } from '../compilation';
+import { Transform } from './base';
+
+export class ToLower extends Transform<frontend.transform.ToLower> {
+ public build(ctx: Compilation, value: string): string {
+ return `((${value}) >= 'A' && (${value}) <= 'Z' ? ` +
+ `(${value} | 0x20) : (${value}))`;
+ }
+}
diff --git a/llparse/test/code-test.ts b/llparse/test/code-test.ts
new file mode 100644
index 0000000..54c3f85
--- /dev/null
+++ b/llparse/test/code-test.ts
@@ -0,0 +1,168 @@
+import * as assert from 'assert';
+
+import { LLParse } from '../src/api';
+
+import { build, NUM_SELECT, printMatch, printOff } from './fixtures';
+
+describe('llparse/code', () => {
+ let p: LLParse;
+
+ beforeEach(() => {
+ p = new LLParse();
+ });
+
+ describe('`.mulAdd()`', () => {
+ it('should operate normally', async () => {
+ const start = p.node('start');
+ const dot = p.node('dot');
+
+ p.property('i64', 'counter');
+
+ const is1337 = p.invoke(p.code.load('counter'), {
+ 1337: printOff(p, p.invoke(p.code.update('counter', 0), start)),
+ }, p.error(1, 'Invalid result'));
+
+ const count = p.invoke(p.code.mulAdd('counter', { base: 10 }), start);
+
+ start
+ .select(NUM_SELECT, count)
+ .otherwise(dot);
+
+ dot
+ .match('.', is1337)
+ .otherwise(p.error(1, 'Unexpected'));
+
+ const binary = await build(p, start, 'mul-add');
+ await binary.check('1337.', 'off=5\n');
+ });
+
+ it('should operate fail on overflow', async () => {
+ const start = p.node('start');
+
+ p.property('i8', 'counter');
+
+ const count = p.invoke(p.code.mulAdd('counter', { base: 10 }), {
+ 1: printOff(p, start),
+ }, start);
+
+ start
+ .select(NUM_SELECT, count)
+ .otherwise(p.error(1, 'Unexpected'));
+
+ const binary = await build(p, start, 'mul-add-overflow');
+ await binary.check('1111', 'off=4\n');
+ });
+
+ it('should operate fail on greater than max', async () => {
+ const start = p.node('start');
+
+ p.property('i64', 'counter');
+
+ const count = p.invoke(p.code.mulAdd('counter', {
+ base: 10,
+ max: 1000,
+ }), {
+ 1: printOff(p, start),
+ }, start);
+
+ start
+ .select(NUM_SELECT, count)
+ .otherwise(p.error(1, 'Unexpected'));
+
+ const binary = await build(p, start, 'mul-add-max-overflow');
+ await binary.check('1111', 'off=4\n');
+ });
+ });
+
+ describe('`.update()`', () => {
+ it('should operate normally', async () => {
+ const start = p.node('start');
+
+ p.property('i64', 'counter');
+
+ const update = p.invoke(p.code.update('counter', 42));
+
+ start
+ .skipTo(update);
+
+ update
+ .otherwise(p.invoke(p.code.load('counter'), {
+ 42: printOff(p, start),
+ }, p.error(1, 'Unexpected')));
+
+ const binary = await build(p, start, 'update');
+ await binary.check('.', 'off=1\n');
+ });
+ });
+
+ describe('`.isEqual()`', () => {
+ it('should operate normally', async () => {
+ const start = p.node('start');
+
+ p.property('i64', 'counter');
+
+ const check = p.invoke(p.code.isEqual('counter', 1), {
+ 0: printOff(p, start),
+ 1: start,
+ }, p.error(1, 'Unexpected'));
+
+ start
+ .select(NUM_SELECT, p.invoke(p.code.store('counter'), check))
+ .otherwise(p.error(1, 'Unexpected'));
+
+ const binary = await build(p, start, 'is-equal');
+ await binary.check('010', 'off=1\noff=3\n');
+ });
+ });
+
+ describe('`.or()`/`.and()`/`.test()`', () => {
+ it('should set and retrieve bits', async () => {
+ const start = p.node('start');
+ const test = p.node('test');
+
+ p.property('i64', 'flag');
+
+ start
+ .match('1', p.invoke(p.code.or('flag', 1), start))
+ .match('2', p.invoke(p.code.or('flag', 2), start))
+ .match('4', p.invoke(p.code.or('flag', 4), start))
+ // Reset
+ .match('r', p.invoke(p.code.update('flag', 0), start))
+ // Partial Reset
+ .match('p', p.invoke(p.code.and('flag', ~1), start))
+ // Test
+ .match('-', test)
+ .otherwise(p.error(1, 'start'));
+
+ test
+ .match('1', p.invoke(p.code.test('flag', 1), {
+ 0: test,
+ 1: printOff(p, test),
+ }, p.error(2, 'test-1')))
+ .match('2', p.invoke(p.code.test('flag', 2), {
+ 0: test,
+ 1: printOff(p, test),
+ }, p.error(3, 'test-2')))
+ .match('4', p.invoke(p.code.test('flag', 4), {
+ 0: test,
+ 1: printOff(p, test),
+ }, p.error(4, 'test-3')))
+ .match('7', p.invoke(p.code.test('flag', 7), {
+ 0: test,
+ 1: printOff(p, test),
+ }, p.error(5, 'test-7')))
+ // Restart
+ .match('.', start)
+ .otherwise(p.error(6, 'test'));
+
+ const binary = await build(p, start, 'or-test');
+ await binary.check('1-124.2-1247.4-1247.r4-124.r12p-12', [
+ 'off=3',
+ 'off=9', 'off=10',
+ 'off=16', 'off=17', 'off=18', 'off=19',
+ 'off=26',
+ 'off=34',
+ ]);
+ });
+ });
+});
diff --git a/llparse/test/compiler-test.ts b/llparse/test/compiler-test.ts
new file mode 100644
index 0000000..39bb69f
--- /dev/null
+++ b/llparse/test/compiler-test.ts
@@ -0,0 +1,289 @@
+import { LLParse } from '../src/api';
+
+import {
+ ALPHA, build, NUM, NUM_SELECT, printMatch, printOff,
+} from './fixtures';
+
+describe('llparse/Compiler', () => {
+ let p: LLParse;
+
+ beforeEach(() => {
+ p = new LLParse();
+ });
+
+ it('should compile simple parser', async () => {
+ const start = p.node('start');
+
+ start.match(' ', start);
+
+ start.match('HTTP', printOff(p, start));
+
+ start.select({
+ CONNECT: 6,
+ DELETE: 4,
+ GET: 1,
+ HEAD: 0,
+ OPTIONS: 5,
+ PATCH: 8,
+ POST: 2,
+ PUT: 3,
+ TRACE: 7,
+ }, printMatch(p, start));
+
+ start.otherwise(p.error(3, 'Invalid word'));
+
+ const binary = await build(p, start, 'simple');
+ await binary.check('GET', 'off=3 match=1\n');
+ });
+
+ it('should optimize shallow select', async () => {
+ const start = p.node('start');
+
+ start.select(NUM_SELECT, printMatch(p, start));
+
+ start.otherwise(p.error(3, 'Invalid word'));
+
+ const binary = await build(p, start, 'shallow');
+ await binary.check('012', 'off=1 match=0\noff=2 match=1\noff=3 match=2\n');
+ });
+
+ it('should support key-value select', async () => {
+ const start = p.node('start');
+
+ start.select('0', 0, printMatch(p, start));
+ start.select('1', 1, printMatch(p, start));
+ start.select('2', 2, printMatch(p, start));
+
+ start.otherwise(p.error(3, 'Invalid word'));
+
+ const binary = await build(p, start, 'kv-select');
+ await binary.check('012', 'off=1 match=0\noff=2 match=1\noff=3 match=2\n');
+ });
+
+ it('should support multi-match', async () => {
+ const start = p.node('start');
+
+ start.match([ ' ', '\t', '\r', '\n' ], start);
+
+ start.select({
+ A: 0,
+ B: 1,
+ }, printMatch(p, start));
+
+ start.otherwise(p.error(3, 'Invalid word'));
+
+ const binary = await build(p, start, 'multi-match');
+ await binary.check(
+ 'A B\t\tA\r\nA',
+ 'off=1 match=0\noff=3 match=1\noff=6 match=0\noff=9 match=0\n');
+ });
+
+ it('should support numeric-match', async () => {
+ const start = p.node('start');
+
+ start.match(32, start);
+
+ start.select({
+ A: 0,
+ B: 1,
+ }, printMatch(p, start));
+
+ start.otherwise(p.error(3, 'Invalid word'));
+
+ const binary = await build(p, start, 'multi-match');
+ await binary.check(
+ 'A B A A',
+ 'off=1 match=0\noff=3 match=1\noff=6 match=0\noff=9 match=0\n');
+ });
+
+ it('should support custom state properties', async () => {
+ const start = p.node('start');
+ const error = p.error(3, 'Invalid word');
+
+ p.property('i8', 'custom');
+
+ const second = p.invoke(p.code.load('custom'), {
+ 0: p.invoke(p.code.match('llparse__print_zero'), { 0: start }, error),
+ 1: p.invoke(p.code.match('llparse__print_one'), { 0: start }, error),
+ }, error);
+
+ start
+ .select({
+ 0: 0,
+ 1: 1,
+ }, p.invoke(p.code.store('custom'), second))
+ .otherwise(error);
+
+ const binary = await build(p, start, 'custom-prop');
+ await binary.check('0110', 'off=1 0\noff=2 1\noff=3 1\noff=4 0\n');
+ });
+
+ it('should return error code/reason', async () => {
+ const start = p.node('start');
+
+ start.match('a', start);
+ start.otherwise(p.error(42, 'some reason'));
+
+ const binary = await build(p, start, 'error');
+ await binary.check('aab', 'off=2 error code=42 reason="some reason"\n');
+ });
+
+ it('should not merge `.match()` with `.peek()`', async () => {
+ const maybeCr = p.node('maybeCr');
+ const lf = p.node('lf');
+
+ maybeCr.peek('\n', lf);
+ maybeCr.match('\r', lf);
+ maybeCr.otherwise(p.error(1, 'error'));
+
+ lf.match('\n', printOff(p, maybeCr));
+ lf.otherwise(p.error(2, 'error'));
+
+ const binary = await build(p, maybeCr, 'no-merge');
+ await binary.check('\r\n\n', 'off=2\noff=3\n');
+ });
+
+ describe('`.match()`', () => {
+ it('should compile to a single-bit table-lookup node', async () => {
+ const start = p.node('start');
+
+ start
+ .match(ALPHA, start)
+ .skipTo(printOff(p, start));
+
+ // TODO(indutny): validate compilation result?
+ const binary = await build(p, start, 'match-bit-check');
+ await binary.check('pecan.is.dead.', 'off=6\noff=9\noff=14\n');
+ });
+
+ it('should compile to a multi-bit table-lookup node', async () => {
+ const start = p.node('start');
+ const another = p.node('another');
+
+ start
+ .match(ALPHA, start)
+ .peek(NUM, another)
+ .skipTo(printOff(p, start));
+
+ another
+ .match(NUM, another)
+ .otherwise(start);
+
+ // TODO(indutny): validate compilation result?
+ const binary = await build(p, start, 'match-multi-bit-check');
+ await binary.check('pecan.135.is.dead.',
+ 'off=6\noff=10\noff=13\noff=18\n');
+ });
+
+ it('should not overflow on signed char in table-lookup node', async () => {
+ const start = p.node('start');
+
+ start
+ .match(ALPHA, start)
+ .match([ 0xc3, 0xbc ], start)
+ .skipTo(printOff(p, start));
+
+ // TODO(indutny): validate compilation result?
+ const binary = await build(p, start, 'match-bit-check');
+ await binary.check('Düsseldorf.', 'off=12\n');
+ });
+
+ it('should match single quotes and forward slashes', async () => {
+ const start = p.node('start');
+
+ start
+ .match('\'', printOff(p, start))
+ .match('\\', printOff(p, start))
+ .otherwise(p.error(3, 'Invalid char'));
+
+ // TODO(indutny): validate compilation result?
+ const binary = await build(p, start, 'escape-char');
+ await binary.check('\\\'', 'off=1\noff=2\n');
+ });
+
+ it('should hit SSE4.2 optimization for table-lookup', async () => {
+ const start = p.node('start');
+
+ start
+ .match(ALPHA, start)
+ .skipTo(printOff(p, start));
+
+ // TODO(indutny): validate compilation result?
+ const binary = await build(p, start, 'match-bit-check-sse');
+ await binary.check('abcdabcdabcdabcdabcdabcdabcd.abcd.',
+ 'off=29\noff=34\n');
+ });
+
+ it('should compile overlapping matches', async () => {
+ const start = p.node('start');
+
+ start.select({
+ aa: 1,
+ aab: 2,
+ }, printMatch(p, start));
+
+ start.otherwise(p.error(3, 'Invalid word'));
+
+ const binary = await build(p, start, 'overlapping-matches');
+ await binary.check('aaaabaa', 'off=2 match=1\noff=5 match=2\n');
+ });
+ });
+
+ describe('`.peek()`', () => {
+ it('should not advance position', async () => {
+ const start = p.node('start');
+ const ab = p.node('ab');
+ const error = p.error(3, 'Invalid word');
+
+ start
+ .peek([ 'a', 'b' ], ab)
+ .otherwise(error);
+
+ ab
+ .match([ 'a', 'b' ], printOff(p, start))
+ .otherwise(error);
+
+ const binary = await build(p, start, 'peek');
+ await binary.check('ab', 'off=1\noff=2\n');
+ });
+ });
+
+ describe('`.otherwise()`', () => {
+ it('should not advance position by default', async () => {
+ const a = p.node('a');
+ const b = p.node('b');
+
+ a
+ .match('A', a)
+ .otherwise(b);
+
+ b
+ .match('B', printOff(p, b))
+ .skipTo(a);
+
+ const binary = await build(p, a, 'otherwise-noadvance');
+ await binary.check('AABAB', 'off=3\noff=5\n');
+ });
+
+ it('should advance when it is `.skipTo()`', async () => {
+ const start = p.node('start');
+
+ start
+ .match(' ', printOff(p, start))
+ .skipTo(start);
+
+ const binary = await build(p, start, 'otherwise-skip');
+ await binary.check('HELLO WORLD', 'off=6\n');
+ });
+
+ it('should skip everything with `.skipTo()`', async () => {
+ const start = p.node('start');
+
+ start
+ .skipTo(start);
+
+ const binary = await build(p, start, 'all-skip');
+ await binary.check('HELLO WORLD', '\n');
+ });
+ });
+});
diff --git a/llparse/test/consume-test.ts b/llparse/test/consume-test.ts
new file mode 100644
index 0000000..f9fb383
--- /dev/null
+++ b/llparse/test/consume-test.ts
@@ -0,0 +1,69 @@
+import * as assert from 'assert';
+
+import { LLParse } from '../src/api';
+
+import { build, NUM_SELECT, printMatch, printOff } from './fixtures';
+
+describe('llparse/consume', () => {
+ let p: LLParse;
+
+ beforeEach(() => {
+ p = new LLParse();
+ });
+
+ it('should consume bytes with i8 field', async () => {
+ p.property('i8', 'to_consume');
+
+ const start = p.node('start');
+ const consume = p.consume('to_consume');
+
+ start.select(NUM_SELECT, p.invoke(p.code.store('to_consume'), consume));
+
+ start
+ .otherwise(p.error(1, 'unexpected'));
+
+ consume
+ .otherwise(printOff(p, start));
+
+ const binary = await build(p, start, 'consume');
+ await binary.check('3aaa2bb1a01b', 'off=4\noff=7\noff=9\noff=10\noff=12\n');
+ });
+
+ it('should consume bytes with i64 field', async () => {
+ p.property('i64', 'to_consume');
+
+ const start = p.node('start');
+ const consume = p.consume('to_consume');
+
+ start.select(NUM_SELECT, p.invoke(p.code.store('to_consume'), consume));
+
+ start
+ .otherwise(p.error(1, 'unexpected'));
+
+ consume
+ .otherwise(printOff(p, start));
+
+ const binary = await build(p, start, 'consume-i64');
+ await binary.check('3aaa2bb1a01b', 'off=4\noff=7\noff=9\noff=10\noff=12\n');
+ });
+
+ it('should consume bytes with untruncated i64 field', async () => {
+ p.property('i64', 'to_consume');
+
+ const start = p.node('start');
+ const consume = p.consume('to_consume');
+
+ start
+ .select(
+ NUM_SELECT,
+ p.invoke(p.code.mulAdd('to_consume', { base: 10 }), start)
+ )
+ .skipTo(consume);
+
+ consume
+ .otherwise(printOff(p, start));
+
+ const binary = await build(p, start, 'consume-untruncated-i64');
+ await binary.check('4294967297.xxxxxxxx', '\n');
+ });
+});
diff --git a/llparse/test/fixtures/extra.c b/llparse/test/fixtures/extra.c
new file mode 100644
index 0000000..79cdff9
--- /dev/null
+++ b/llparse/test/fixtures/extra.c
@@ -0,0 +1,84 @@
+#include "fixture.h"
+
+int llparse__print_zero(llparse_t* s, const char* p, const char* endp) {
+ if (llparse__in_bench)
+ return 0;
+ llparse__print(p, endp, "0");
+ return 0;
+}
+
+
+int llparse__print_one(llparse_t* s, const char* p, const char* endp) {
+ if (llparse__in_bench)
+ return 0;
+ llparse__print(p, endp, "1");
+ return 0;
+}
+
+
+int llparse__print_off(llparse_t* s, const char* p, const char* endp) {
+ if (llparse__in_bench)
+ return 0;
+ llparse__print(p, endp, "");
+ return 0;
+}
+
+
+int llparse__print_match(llparse_t* s, const char* p, const char* endp,
+ int value) {
+ if (llparse__in_bench)
+ return 0;
+ llparse__print(p, endp, "match=%d", value);
+ return 0;
+}
+
+
+int llparse__on_dot(llparse_t* s, const char* p, const char* endp) {
+ if (llparse__in_bench)
+ return 0;
+ return llparse__print_span("dot", p, endp);
+}
+
+
+int llparse__on_dash(llparse_t* s, const char* p, const char* endp) {
+ if (llparse__in_bench)
+ return 0;
+ return llparse__print_span("dash", p, endp);
+}
+
+
+int llparse__on_underscore(llparse_t* s, const char* p,
+ const char* endp) {
+ if (llparse__in_bench)
+ return 0;
+ return llparse__print_span("underscore", p, endp);
+}
+
+
+/* A span callback, really */
+int llparse__please_fail(llparse_t* s, const char* p, const char* endp) {
+ s->reason = "please fail";
+ if (llparse__in_bench)
+ return 1;
+ return 1;
+}
+
+
+/* A span callback, really */
+static int llparse__pause_once_counter;
+
+int llparse__pause_once(llparse_t* s, const char* p, const char* endp) {
+ if (!llparse__in_bench)
+ llparse__print_span("pause", p, endp);
+
+ if (llparse__pause_once_counter != 0)
+ return 0;
+ llparse__pause_once_counter = 1;
+
+ return LLPARSE__ERROR_PAUSE;
+}
+
+
+int llparse__test_init() {
+ llparse__pause_once_counter = 0;
+}
diff --git a/llparse/test/fixtures/index.ts b/llparse/test/fixtures/index.ts
new file mode 100644
index 0000000..d8a7336
--- /dev/null
+++ b/llparse/test/fixtures/index.ts
@@ -0,0 +1,52 @@
+import { source } from 'llparse-frontend';
+import { Fixture, FixtureResult } from 'llparse-test-fixture';
+import * as path from 'path';
+
+import { LLParse } from '../../src/api';
+
+export { ERROR_PAUSE } from 'llparse-test-fixture';
+
+const fixtures = new Fixture({
+ buildDir: path.join(__dirname, '..', 'tmp'),
+ extra: [
+ '-msse4.2',
+ '-DLLPARSE__TEST_INIT=llparse__test_init',
+ path.join(__dirname, 'extra.c'),
+ ],
+});
+
+export function build(llparse: LLParse, node: source.node.Node, outFile: string)
+ : Promise<FixtureResult> {
+ return fixtures.build(llparse.build(node, {
+ c: {
+ header: outFile,
+ },
+ }), outFile);
+}
+
+export function printMatch(p: LLParse, next: source.node.Node)
+ : source.node.Node {
+ const code = p.code.value('llparse__print_match');
+ const res = p.invoke(code, next);
+ return res;
+}
+
+export function printOff(p: LLParse, next: source.node.Node): source.node.Node {
+ const code = p.code.match('llparse__print_off');
+ return p.invoke(code, next);
+}
+
+export const NUM_SELECT: { readonly [key: string]: number } = {
+ 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
+};
+
+export const NUM: ReadonlyArray<string> = [
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+];
+
+export const ALPHA: ReadonlyArray<string> = [
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+];
diff --git a/llparse/test/resumption-test.ts b/llparse/test/resumption-test.ts
new file mode 100644
index 0000000..438b7fd
--- /dev/null
+++ b/llparse/test/resumption-test.ts
@@ -0,0 +1,55 @@
+import * as assert from 'assert';
+
+import { LLParse } from '../src/api';
+
+import { build, ERROR_PAUSE, printMatch, printOff } from './fixtures';
+
+describe('llparse/resumption', () => {
+ let p: LLParse;
+
+ beforeEach(() => {
+ p = new LLParse();
+ });
+
+ it('should resume after span end pause', async () => {
+ const start = p.node('start');
+ const a = p.node('a');
+ const span = p.span(p.code.span('llparse__pause_once'));
+
+ start
+ .peek('a', span.start(a))
+ .skipTo(start);
+
+ a
+ .match('a', a)
+ .otherwise(span.end(start));
+
+ const binary = await build(p, start, 'resume-span');
+
+ await binary.check('baaab',
+ new RegExp(
+ '^(' +
+ 'off=\\d+ pause\\noff=1 len=3 span\\[pause\\]="aaa"' +
+ '|' +
+ 'off=1 len=3 span\\[pause\\]="aaa"\noff=4 pause' +
+ ')\\n$'
+ , 'g'));
+ });
+
+ it('should resume after `pause` node', async () => {
+ const start = p.node('start');
+ const pause = p.pause(ERROR_PAUSE, 'paused');
+
+ start
+ .match('p', pause)
+ .skipTo(start);
+
+ pause
+ .otherwise(printOff(p, start));
+
+ const binary = await build(p, start, 'resume-pause');
+
+ await binary.check('..p....p..',
+ 'off=3 pause\noff=3\noff=8 pause\noff=8\n');
+ });
+});
diff --git a/llparse/test/span-test.ts b/llparse/test/span-test.ts
new file mode 100644
index 0000000..b01ad51
--- /dev/null
+++ b/llparse/test/span-test.ts
@@ -0,0 +1,107 @@
+import * as assert from 'assert';
+
+import { LLParse } from '../src/api';
+
+import { build, printMatch, printOff } from './fixtures';
+
+describe('llparse/spans', () => {
+ let p: LLParse;
+
+ beforeEach(() => {
+ p = new LLParse();
+ });
+
+ it('should invoke span callback', async () => {
+ const start = p.node('start');
+ const dot = p.node('dot');
+ const dash = p.node('dash');
+ const underscore = p.node('underscore');
+
+ const span = {
+ dash: p.span(p.code.span('llparse__on_dash')),
+ dot: p.span(p.code.span('llparse__on_dot')),
+ underscore: p.span(p.code.span('llparse__on_underscore')),
+ };
+
+ start.otherwise(span.dot.start(dot));
+
+ dot
+ .match('.', dot)
+ .peek('-', span.dash.start(dash))
+ .peek('_', span.underscore.start(underscore))
+ .skipTo(span.dot.end(start));
+
+ dash
+ .match('-', dash)
+ .otherwise(span.dash.end(dot));
+
+ underscore
+ .match('_', underscore)
+ .otherwise(span.underscore.end(dot));
+
+ const binary = await build(p, start, 'span');
+ await binary.check('..--..__..',
+ 'off=2 len=2 span[dash]="--"\n' +
+ 'off=6 len=2 span[underscore]="__"\n' +
+ 'off=0 len=10 span[dot]="..--..__.."\n');
+ });
+
+ it('should return error', async () => {
+ const start = p.node('start');
+ const dot = p.node('dot');
+
+ const span = {
+ pleaseFail: p.span(p.code.span('llparse__please_fail')),
+ };
+
+ start.otherwise(span.pleaseFail.start(dot));
+
+ dot
+ .match('.', dot)
+ .skipTo(span.pleaseFail.end(start));
+
+ const binary = await build(p, start, 'span-error');
+
+ await binary.check(
+ '....a',
+ /off=\d+ error code=1 reason="please fail"\n/);
+ });
+
+ it('should return error at `executeSpans()`', async () => {
+ const start = p.node('start');
+ const dot = p.node('dot');
+
+ const span = {
+ pleaseFail: p.span(p.code.span('llparse__please_fail')),
+ };
+
+ start.otherwise(span.pleaseFail.start(dot));
+
+ dot
+ .match('.', dot)
+ .skipTo(span.pleaseFail.end(start));
+
+ const binary = await build(p, start, 'span-error-execute');
+
+ await binary.check(
+ '.........',
+ /off=9 error code=1 reason="please fail"\n/, { scan: 100 });
+ });
+
+ it('should not invoke spurious span callback', async () => {
+ const start = p.node('start');
+ const dot = p.node('dot');
+ const span = p.span(p.code.span('llparse__on_dot'));
+
+ start
+ .match('hello', span.start(dot))
+ .skipTo(start);
+
+ dot
+ .match('.', dot)
+ .skipTo(span.end(start));
+
+ const binary = await build(p, start, 'span-spurious');
+ await binary.check('hello', [ '' ]);
+ });
+});
diff --git a/llparse/test/transform-test.ts b/llparse/test/transform-test.ts
new file mode 100644
index 0000000..d30381e
--- /dev/null
+++ b/llparse/test/transform-test.ts
@@ -0,0 +1,41 @@
+import * as assert from 'assert';
+
+import { LLParse } from '../src/api';
+
+import { build, printMatch, printOff } from './fixtures';
+
+describe('llparse/transform', () => {
+ let p: LLParse;
+
+ beforeEach(() => {
+ p = new LLParse();
+ });
+
+ it('should apply transformation before the match', async () => {
+ const start = p.node('start');
+
+ start
+ .transform(p.transform.toLowerUnsafe())
+ .match('connect', printOff(p, start))
+ .match('close', printOff(p, start))
+ .otherwise(p.error(1, 'error'));
+
+ const binary = await build(p, start, 'transform-lower');
+ await binary.check('connectCLOSEcOnNeCt', 'off=7\noff=12\noff=19\n');
+ });
+
+ it('should apply safe `toLower()` transformation', async () => {
+ const start = p.node('start');
+
+ start
+ .transform(p.transform.toLower())
+ .select({
+ 'a-b': 1,
+ 'a\rb': 2,
+ }, printMatch(p, start))
+ .otherwise(p.error(1, 'error'));
+
+ const binary = await build(p, start, 'transform-safe-lower');
+ await binary.check('A-ba\rB', 'off=3 match=1\noff=6 match=2\n');
+ });
+});
diff --git a/llparse/tsconfig.json b/llparse/tsconfig.json
new file mode 100644
index 0000000..01ec7c2
--- /dev/null
+++ b/llparse/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "target": "es2017",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "./lib",
+ "declaration": true,
+ "pretty": true,
+ "sourceMap": true
+ },
+ "include": [
+ "src/**/*.ts"
+ ]
+}
diff --git a/llparse/tslint.json b/llparse/tslint.json
new file mode 100644
index 0000000..24fec09
--- /dev/null
+++ b/llparse/tslint.json
@@ -0,0 +1,16 @@
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {
+ "no-bitwise": null,
+ "max-line-length": [true, 80],
+ "max-classes-per-file": [true, 1, "exclude-class-expressions"],
+ "quotemark": [
+ true, "single", "avoid-escape", "avoid-template"
+ ]
+ },
+ "rulesDirectory": []
+}