Import node-undici_5.28.4+dfsg1+~cs23.12.11.orig-llparse-builder.tar.xz
authorYadd <yadd@debian.org>
Fri, 5 Apr 2024 11:26:06 +0000 (15:26 +0400)
committerYadd <yadd@debian.org>
Fri, 5 Apr 2024 11:26:06 +0000 (15:26 +0400)
[dgit import orig node-undici_5.28.4+dfsg1+~cs23.12.11.orig-llparse-builder.tar.xz]

49 files changed:
.gitignore [new file with mode: 0644]
.travis.yml [new file with mode: 0644]
README.md [new file with mode: 0644]
package-lock.json [new file with mode: 0644]
package.json [new file with mode: 0644]
src/builder.ts [new file with mode: 0644]
src/code/and.ts [new file with mode: 0644]
src/code/base.ts [new file with mode: 0644]
src/code/creator.ts [new file with mode: 0644]
src/code/field-value.ts [new file with mode: 0644]
src/code/field.ts [new file with mode: 0644]
src/code/index.ts [new file with mode: 0644]
src/code/is-equal.ts [new file with mode: 0644]
src/code/load.ts [new file with mode: 0644]
src/code/match.ts [new file with mode: 0644]
src/code/mul-add.ts [new file with mode: 0644]
src/code/or.ts [new file with mode: 0644]
src/code/span.ts [new file with mode: 0644]
src/code/store.ts [new file with mode: 0644]
src/code/test.ts [new file with mode: 0644]
src/code/update.ts [new file with mode: 0644]
src/code/value.ts [new file with mode: 0644]
src/edge.ts [new file with mode: 0644]
src/loop-checker/index.ts [new file with mode: 0644]
src/loop-checker/lattice.ts [new file with mode: 0644]
src/node/base.ts [new file with mode: 0644]
src/node/consume.ts [new file with mode: 0644]
src/node/error.ts [new file with mode: 0644]
src/node/index.ts [new file with mode: 0644]
src/node/invoke.ts [new file with mode: 0644]
src/node/match.ts [new file with mode: 0644]
src/node/pause.ts [new file with mode: 0644]
src/node/span-end.ts [new file with mode: 0644]
src/node/span-start.ts [new file with mode: 0644]
src/property.ts [new file with mode: 0644]
src/reachability.ts [new file with mode: 0644]
src/span-allocator.ts [new file with mode: 0644]
src/span.ts [new file with mode: 0644]
src/transform/base.ts [new file with mode: 0644]
src/transform/creator.ts [new file with mode: 0644]
src/transform/index.ts [new file with mode: 0644]
src/transform/to-lower-unsafe.ts [new file with mode: 0644]
src/transform/to-lower.ts [new file with mode: 0644]
src/utils.ts [new file with mode: 0644]
test/builder-test.ts [new file with mode: 0644]
test/loop-checker-test.ts [new file with mode: 0644]
test/span-allocator-test.ts [new file with mode: 0644]
tsconfig.json [new file with mode: 0644]
tslint.json [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5e67ab3
--- /dev/null
@@ -0,0 +1,3 @@
+node_modules/
+npm-debug.log
+lib/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..b5efd79
--- /dev/null
@@ -0,0 +1,4 @@
+sudo: false
+language: node_js
+node_js:
+  - "stable"
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..522fba2
--- /dev/null
+++ b/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/package-lock.json b/package-lock.json
new file mode 100644 (file)
index 0000000..5e76f34
--- /dev/null
@@ -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/package.json b/package.json
new file mode 100644 (file)
index 0000000..1c1ac54
--- /dev/null
@@ -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/src/builder.ts b/src/builder.ts
new file mode 100644 (file)
index 0000000..a335a85
--- /dev/null
@@ -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/src/code/and.ts b/src/code/and.ts
new file mode 100644 (file)
index 0000000..5f78675
--- /dev/null
@@ -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/src/code/base.ts b/src/code/base.ts
new file mode 100644 (file)
index 0000000..00b479f
--- /dev/null
@@ -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/src/code/creator.ts b/src/code/creator.ts
new file mode 100644 (file)
index 0000000..98f9296
--- /dev/null
@@ -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/src/code/field-value.ts b/src/code/field-value.ts
new file mode 100644 (file)
index 0000000..2ceea69
--- /dev/null
@@ -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/src/code/field.ts b/src/code/field.ts
new file mode 100644 (file)
index 0000000..af58c84
--- /dev/null
@@ -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/src/code/index.ts b/src/code/index.ts
new file mode 100644 (file)
index 0000000..7a651e3
--- /dev/null
@@ -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/src/code/is-equal.ts b/src/code/is-equal.ts
new file mode 100644 (file)
index 0000000..91bb957
--- /dev/null
@@ -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/src/code/load.ts b/src/code/load.ts
new file mode 100644 (file)
index 0000000..9f3df2e
--- /dev/null
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Load extends Field {
+  constructor(field: string) {
+    super('match', 'load', field);
+  }
+}
diff --git a/src/code/match.ts b/src/code/match.ts
new file mode 100644 (file)
index 0000000..631376a
--- /dev/null
@@ -0,0 +1,7 @@
+import { Code } from './base';
+
+export class Match extends Code {
+  constructor(name: string) {
+    super('match', name);
+  }
+}
diff --git a/src/code/mul-add.ts b/src/code/mul-add.ts
new file mode 100644 (file)
index 0000000..fd648ed
--- /dev/null
@@ -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/src/code/or.ts b/src/code/or.ts
new file mode 100644 (file)
index 0000000..33bd402
--- /dev/null
@@ -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/src/code/span.ts b/src/code/span.ts
new file mode 100644 (file)
index 0000000..b97e09e
--- /dev/null
@@ -0,0 +1,5 @@
+import { Match } from './match';
+
+export class Span extends Match {
+  // no-op
+}
diff --git a/src/code/store.ts b/src/code/store.ts
new file mode 100644 (file)
index 0000000..84abfef
--- /dev/null
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Store extends Field {
+  constructor(field: string) {
+    super('value', 'store', field);
+  }
+}
diff --git a/src/code/test.ts b/src/code/test.ts
new file mode 100644 (file)
index 0000000..a9d0a22
--- /dev/null
@@ -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/src/code/update.ts b/src/code/update.ts
new file mode 100644 (file)
index 0000000..de62476
--- /dev/null
@@ -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/src/code/value.ts b/src/code/value.ts
new file mode 100644 (file)
index 0000000..06c6fd7
--- /dev/null
@@ -0,0 +1,7 @@
+import { Code } from './base';
+
+export class Value extends Code {
+  constructor(name: string) {
+    super('value', name);
+  }
+}
diff --git a/src/edge.ts b/src/edge.ts
new file mode 100644 (file)
index 0000000..f6b55cc
--- /dev/null
@@ -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/src/loop-checker/index.ts b/src/loop-checker/index.ts
new file mode 100644 (file)
index 0000000..5751955
--- /dev/null
@@ -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/src/loop-checker/lattice.ts b/src/loop-checker/lattice.ts
new file mode 100644 (file)
index 0000000..8d2a7fe
--- /dev/null
@@ -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/src/node/base.ts b/src/node/base.ts
new file mode 100644 (file)
index 0000000..9840f16
--- /dev/null
@@ -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/src/node/consume.ts b/src/node/consume.ts
new file mode 100644 (file)
index 0000000..eff4037
--- /dev/null
@@ -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/src/node/error.ts b/src/node/error.ts
new file mode 100644 (file)
index 0000000..393f566
--- /dev/null
@@ -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/src/node/index.ts b/src/node/index.ts
new file mode 100644 (file)
index 0000000..e3d5fe5
--- /dev/null
@@ -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/src/node/invoke.ts b/src/node/invoke.ts
new file mode 100644 (file)
index 0000000..d6791a7
--- /dev/null
@@ -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/src/node/match.ts b/src/node/match.ts
new file mode 100644 (file)
index 0000000..617a659
--- /dev/null
@@ -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/src/node/pause.ts b/src/node/pause.ts
new file mode 100644 (file)
index 0000000..2dcf5d1
--- /dev/null
@@ -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/src/node/span-end.ts b/src/node/span-end.ts
new file mode 100644 (file)
index 0000000..377cd73
--- /dev/null
@@ -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/src/node/span-start.ts b/src/node/span-start.ts
new file mode 100644 (file)
index 0000000..f81b432
--- /dev/null
@@ -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/src/property.ts b/src/property.ts
new file mode 100644 (file)
index 0000000..cf2fe4b
--- /dev/null
@@ -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/src/reachability.ts b/src/reachability.ts
new file mode 100644 (file)
index 0000000..88bcd65
--- /dev/null
@@ -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/src/span-allocator.ts b/src/span-allocator.ts
new file mode 100644 (file)
index 0000000..b3e8f6b
--- /dev/null
@@ -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/src/span.ts b/src/span.ts
new file mode 100644 (file)
index 0000000..99cafb0
--- /dev/null
@@ -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/src/transform/base.ts b/src/transform/base.ts
new file mode 100644 (file)
index 0000000..902199c
--- /dev/null
@@ -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/src/transform/creator.ts b/src/transform/creator.ts
new file mode 100644 (file)
index 0000000..eaf3d5c
--- /dev/null
@@ -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/src/transform/index.ts b/src/transform/index.ts
new file mode 100644 (file)
index 0000000..acdcf01
--- /dev/null
@@ -0,0 +1,3 @@
+export { Transform } from './base';
+export { Creator } from './creator';
+export { ToLowerUnsafe } from './to-lower-unsafe';
diff --git a/src/transform/to-lower-unsafe.ts b/src/transform/to-lower-unsafe.ts
new file mode 100644 (file)
index 0000000..99d9618
--- /dev/null
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform {
+  constructor() {
+    super('to_lower_unsafe');
+  }
+}
diff --git a/src/transform/to-lower.ts b/src/transform/to-lower.ts
new file mode 100644 (file)
index 0000000..b333fce
--- /dev/null
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ToLower extends Transform {
+  constructor() {
+    super('to_lower');
+  }
+}
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644 (file)
index 0000000..3521b20
--- /dev/null
@@ -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/test/builder-test.ts b/test/builder-test.ts
new file mode 100644 (file)
index 0000000..82723ec
--- /dev/null
@@ -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/test/loop-checker-test.ts b/test/loop-checker-test.ts
new file mode 100644 (file)
index 0000000..0df6064
--- /dev/null
@@ -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/test/span-allocator-test.ts b/test/span-allocator-test.ts
new file mode 100644 (file)
index 0000000..bc8f656
--- /dev/null
@@ -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/tsconfig.json b/tsconfig.json
new file mode 100644 (file)
index 0000000..01ec7c2
--- /dev/null
@@ -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/tslint.json b/tslint.json
new file mode 100644 (file)
index 0000000..b0aaf97
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "defaultSeverity": "error",
+    "extends": [
+        "tslint:recommended"
+    ],
+    "jsRules": {},
+    "rules": {
+      "no-bitwise": null,
+      "quotemark": [
+        true, "single", "avoid-escape", "avoid-template"
+      ]
+    },
+    "rulesDirectory": []
+}