Import node-undici_7.3.0+dfsg1+~cs24.12.11.orig-llparse-frontend.tar.xz
authorJérémy Lal <kapouer@melix.org>
Sun, 26 Jan 2025 15:18:40 +0000 (16:18 +0100)
committerJérémy Lal <kapouer@melix.org>
Sun, 26 Jan 2025 15:18:40 +0000 (16:18 +0100)
[dgit import orig node-undici_7.3.0+dfsg1+~cs24.12.11.orig-llparse-frontend.tar.xz]

126 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/code/and.ts [new file with mode: 0644]
src/code/base.ts [new file with mode: 0644]
src/code/external.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/container/index.ts [new file with mode: 0644]
src/container/wrap.ts [new file with mode: 0644]
src/enumerator.ts [new file with mode: 0644]
src/frontend.ts [new file with mode: 0644]
src/implementation/code.ts [new file with mode: 0644]
src/implementation/full.ts [new file with mode: 0644]
src/implementation/index.ts [new file with mode: 0644]
src/implementation/node.ts [new file with mode: 0644]
src/implementation/transform.ts [new file with mode: 0644]
src/namespace/frontend.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/empty.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/sequence.ts [new file with mode: 0644]
src/node/single.ts [new file with mode: 0644]
src/node/slot.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/node/table-lookup.ts [new file with mode: 0644]
src/peephole.ts [new file with mode: 0644]
src/span-field.ts [new file with mode: 0644]
src/transform/base.ts [new file with mode: 0644]
src/transform/id.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/trie/empty.ts [new file with mode: 0644]
src/trie/index.ts [new file with mode: 0644]
src/trie/node.ts [new file with mode: 0644]
src/trie/sequence.ts [new file with mode: 0644]
src/trie/single.ts [new file with mode: 0644]
src/utils/identifier.ts [new file with mode: 0644]
src/utils/index.ts [new file with mode: 0644]
src/wrap.ts [new file with mode: 0644]
test/container-test.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/and.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/base.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/index.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/is-equal.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/load.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/match.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/mul-add.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/or.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/span.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/store.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/test.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/update.ts [new file with mode: 0644]
test/fixtures/a-implementation/code/value.ts [new file with mode: 0644]
test/fixtures/a-implementation/index.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/base.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/consume.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/empty.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/error.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/index.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/invoke.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/pause.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/sequence.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/single.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/span-end.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/span-start.ts [new file with mode: 0644]
test/fixtures/a-implementation/node/table-lookup.ts [new file with mode: 0644]
test/fixtures/a-implementation/transform/base.ts [new file with mode: 0644]
test/fixtures/a-implementation/transform/id.ts [new file with mode: 0644]
test/fixtures/a-implementation/transform/index.ts [new file with mode: 0644]
test/fixtures/a-implementation/transform/to-lower-unsafe.ts [new file with mode: 0644]
test/fixtures/a-implementation/transform/to-lower.ts [new file with mode: 0644]
test/fixtures/implementation/code/and.ts [new file with mode: 0644]
test/fixtures/implementation/code/base.ts [new file with mode: 0644]
test/fixtures/implementation/code/index.ts [new file with mode: 0644]
test/fixtures/implementation/code/is-equal.ts [new file with mode: 0644]
test/fixtures/implementation/code/load.ts [new file with mode: 0644]
test/fixtures/implementation/code/match.ts [new file with mode: 0644]
test/fixtures/implementation/code/mul-add.ts [new file with mode: 0644]
test/fixtures/implementation/code/or.ts [new file with mode: 0644]
test/fixtures/implementation/code/span.ts [new file with mode: 0644]
test/fixtures/implementation/code/store.ts [new file with mode: 0644]
test/fixtures/implementation/code/test.ts [new file with mode: 0644]
test/fixtures/implementation/code/update.ts [new file with mode: 0644]
test/fixtures/implementation/code/value.ts [new file with mode: 0644]
test/fixtures/implementation/index.ts [new file with mode: 0644]
test/fixtures/implementation/node/base.ts [new file with mode: 0644]
test/fixtures/implementation/node/consume.ts [new file with mode: 0644]
test/fixtures/implementation/node/empty.ts [new file with mode: 0644]
test/fixtures/implementation/node/error.ts [new file with mode: 0644]
test/fixtures/implementation/node/index.ts [new file with mode: 0644]
test/fixtures/implementation/node/invoke.ts [new file with mode: 0644]
test/fixtures/implementation/node/pause.ts [new file with mode: 0644]
test/fixtures/implementation/node/sequence.ts [new file with mode: 0644]
test/fixtures/implementation/node/single.ts [new file with mode: 0644]
test/fixtures/implementation/node/span-end.ts [new file with mode: 0644]
test/fixtures/implementation/node/span-start.ts [new file with mode: 0644]
test/fixtures/implementation/node/table-lookup.ts [new file with mode: 0644]
test/fixtures/implementation/transform/base.ts [new file with mode: 0644]
test/fixtures/implementation/transform/id.ts [new file with mode: 0644]
test/fixtures/implementation/transform/index.ts [new file with mode: 0644]
test/fixtures/implementation/transform/to-lower-unsafe.ts [new file with mode: 0644]
test/fixtures/implementation/transform/to-lower.ts [new file with mode: 0644]
test/frontend-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..88edb62
--- /dev/null
@@ -0,0 +1,2 @@
+node_modules/
+lib/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..03f4af5
--- /dev/null
@@ -0,0 +1,6 @@
+sudo: false
+language: node_js
+node_js:
+  - "stable"
+script:
+  npm test
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..359dd9b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+# llparse-frontend
+[![Build Status](https://secure.travis-ci.org/indutny/llparse-frontend.svg)](http://travis-ci.org/indutny/llparse-frontend)
+[![NPM version](https://badge.fury.io/js/llparse-frontend.svg)](https://badge.fury.io/js/llparse-frontend)
+
+WIP
+
+#### LICENSE
+
+This software is licensed under the MIT License.
+
+Copyright Fedor Indutny, 2018.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/package-lock.json b/package-lock.json
new file mode 100644 (file)
index 0000000..3cfef7a
--- /dev/null
@@ -0,0 +1,1516 @@
+{
+  "name": "llparse-frontend",
+  "version": "3.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.10.3",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz",
+      "integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.10.3"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.10.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
+      "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==",
+      "dev": true
+    },
+    "@babel/highlight": {
+      "version": "7.10.3",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz",
+      "integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.10.3",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      }
+    },
+    "@types/debug": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+      "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
+      "dev": true
+    },
+    "@types/mocha": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
+      "integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "14.11.8",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.8.tgz",
+      "integrity": "sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==",
+      "dev": true
+    },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^1.9.0"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "requires": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "array.prototype.map": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
+      "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1",
+        "es-array-method-boxes-properly": "^1.0.0",
+        "is-string": "^1.0.4"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+      "dev": true
+    },
+    "binary-search": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz",
+      "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA=="
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      }
+    },
+    "chokidar": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+      "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "fsevents": "~2.1.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.4.0"
+      }
+    },
+    "cliui": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^3.1.0",
+        "strip-ansi": "^5.2.0",
+        "wrap-ansi": "^5.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "requires": {
+        "color-name": "1.1.3"
+      }
+    },
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.15.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+      "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+      "dev": true
+    },
+    "debug": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+      "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+      "requires": {
+        "ms": "^2.1.1"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
+    "emoji-regex": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+      "dev": true
+    },
+    "es-abstract": {
+      "version": "1.17.7",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+      "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+      "requires": {
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1",
+        "is-callable": "^1.2.2",
+        "is-regex": "^1.1.1",
+        "object-inspect": "^1.8.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.1",
+        "string.prototype.trimend": "^1.0.1",
+        "string.prototype.trimstart": "^1.0.1"
+      },
+      "dependencies": {
+        "es-abstract": {
+          "version": "1.18.0-next.1",
+          "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+          "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+          "requires": {
+            "es-to-primitive": "^1.2.1",
+            "function-bind": "^1.1.1",
+            "has": "^1.0.3",
+            "has-symbols": "^1.0.1",
+            "is-callable": "^1.2.2",
+            "is-negative-zero": "^2.0.0",
+            "is-regex": "^1.1.1",
+            "object-inspect": "^1.8.0",
+            "object-keys": "^1.1.1",
+            "object.assign": "^4.1.1",
+            "string.prototype.trimend": "^1.0.1",
+            "string.prototype.trimstart": "^1.0.1"
+          }
+        },
+        "object.assign": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+          "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+          "requires": {
+            "define-properties": "^1.1.3",
+            "es-abstract": "^1.18.0-next.0",
+            "has-symbols": "^1.0.1",
+            "object-keys": "^1.1.1"
+          },
+          "dependencies": {
+            "es-abstract": {
+              "version": "1.18.0-next.1",
+              "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+              "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+              "requires": {
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1",
+                "is-callable": "^1.2.2",
+                "is-negative-zero": "^2.0.0",
+                "is-regex": "^1.1.1",
+                "object-inspect": "^1.8.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.1",
+                "string.prototype.trimend": "^1.0.1",
+                "string.prototype.trimstart": "^1.0.1"
+              }
+            }
+          }
+        }
+      }
+    },
+    "es-array-method-boxes-properly": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+      "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+      "dev": true
+    },
+    "es-get-iterator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
+      "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
+      "dev": true,
+      "requires": {
+        "es-abstract": "^1.17.4",
+        "has-symbols": "^1.0.1",
+        "is-arguments": "^1.0.4",
+        "is-map": "^2.0.1",
+        "is-set": "^2.0.1",
+        "is-string": "^1.0.5",
+        "isarray": "^2.0.5"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "requires": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      }
+    },
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+      "dev": true
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
+    "flat": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+      "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+      "dev": true,
+      "requires": {
+        "is-buffer": "~2.0.3"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
+    },
+    "glob": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "growl": {
+      "version": "1.10.5",
+      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+      "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+      "dev": true
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+      "dev": true
+    },
+    "is-arguments": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+      "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-buffer": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+      "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+      "dev": true
+    },
+    "is-callable": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+      "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
+    },
+    "is-date-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-map": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
+      "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
+      "dev": true
+    },
+    "is-negative-zero": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+      "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-plain-obj": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+      "dev": true
+    },
+    "is-regex": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+      "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "is-set": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
+      "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
+      "dev": true
+    },
+    "is-string": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
+      "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
+      "dev": true
+    },
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+      "requires": {
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "iterate-iterator": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
+      "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
+      "dev": true
+    },
+    "iterate-value": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
+      "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
+      "dev": true,
+      "requires": {
+        "es-get-iterator": "^1.0.2",
+        "iterate-iterator": "^1.0.1"
+      }
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "3.14.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+      "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+      "dev": true,
+      "requires": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      }
+    },
+    "llparse-builder": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/llparse-builder/-/llparse-builder-1.5.2.tgz",
+      "integrity": "sha512-i862UNC3YUEdlfK/NUCJxlKjtWjgAI9AJXDRgjcfRHfwFt4Sf8eFPTRsc91/2R9MBZ0kyFdfhi8SVhMsZf1gNQ==",
+      "requires": {
+        "@types/debug": "4.1.5  ",
+        "binary-search": "^1.3.6",
+        "debug": "^4.2.0"
+      },
+      "dependencies": {
+        "@types/debug": {
+          "version": "4.1.5",
+          "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+          "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ=="
+        },
+        "debug": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+          "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+          "requires": {
+            "ms": "2.1.2"
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        }
+      }
+    },
+    "locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^5.0.0"
+      }
+    },
+    "log-symbols": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+      "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "mkdirp": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+      "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "1.2.5",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+          "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+          "dev": true
+        }
+      }
+    },
+    "mocha": {
+      "version": "8.1.3",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz",
+      "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "4.1.1",
+        "browser-stdout": "1.3.1",
+        "chokidar": "3.4.2",
+        "debug": "4.1.1",
+        "diff": "4.0.2",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
+        "glob": "7.1.6",
+        "growl": "1.10.5",
+        "he": "1.2.0",
+        "js-yaml": "3.14.0",
+        "log-symbols": "4.0.0",
+        "minimatch": "3.0.4",
+        "ms": "2.1.2",
+        "object.assign": "4.1.0",
+        "promise.allsettled": "1.0.2",
+        "serialize-javascript": "4.0.0",
+        "strip-json-comments": "3.0.1",
+        "supports-color": "7.1.0",
+        "which": "2.0.2",
+        "wide-align": "1.1.3",
+        "workerpool": "6.0.0",
+        "yargs": "13.3.2",
+        "yargs-parser": "13.1.2",
+        "yargs-unparser": "1.6.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "diff": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+          "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+          "dev": true
+        },
+        "escape-string-regexp": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+          "dev": true
+        },
+        "glob": {
+          "version": "7.1.6",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+          "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "ms": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+          "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "ms": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "object-inspect": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+    },
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "p-limit": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+      "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+      "dev": true,
+      "requires": {
+        "p-try": "^2.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^3.0.2"
+      }
+    },
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true
+    },
+    "path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+      "dev": true
+    },
+    "promise.allsettled": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
+      "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
+      "dev": true,
+      "requires": {
+        "array.prototype.map": "^1.0.1",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1",
+        "function-bind": "^1.1.1",
+        "iterate-value": "^1.0.0"
+      }
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "readdirp": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+      "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+      "dev": true,
+      "requires": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true
+    },
+    "serialize-javascript": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+      "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+      "dev": true
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true
+    },
+    "source-map-support": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+      "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+      "dev": true
+    },
+    "string-width": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+      "dev": true,
+      "requires": {
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^4.0.0"
+      }
+    },
+    "string.prototype.trimend": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+      "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "string.prototype.trimstart": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+      "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "strip-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^3.0.0"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+      "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+      "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^3.0.0"
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "ts-node": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+      "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
+      "dev": true,
+      "requires": {
+        "arg": "^4.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "source-map-support": "^0.5.17",
+        "yn": "3.1.1"
+      }
+    },
+    "tslib": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+      "dev": true
+    },
+    "tslint": {
+      "version": "5.20.1",
+      "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
+      "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "builtin-modules": "^1.1.1",
+        "chalk": "^2.3.0",
+        "commander": "^2.12.1",
+        "diff": "^4.0.1",
+        "glob": "^7.1.1",
+        "js-yaml": "^3.13.1",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "resolve": "^1.3.2",
+        "semver": "^5.3.0",
+        "tslib": "^1.8.0",
+        "tsutils": "^2.29.0"
+      },
+      "dependencies": {
+        "diff": {
+          "version": "4.0.2",
+          "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+          "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+          "dev": true
+        }
+      }
+    },
+    "tsutils": {
+      "version": "2.29.0",
+      "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+      "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.8.1"
+      }
+    },
+    "typescript": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
+      "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
+      "dev": true
+    },
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+      "dev": true
+    },
+    "wide-align": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^1.0.2 || 2"
+      }
+    },
+    "workerpool": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
+      "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.0",
+        "string-width": "^3.0.0",
+        "strip-ansi": "^5.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "y18n": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+      "dev": true
+    },
+    "yargs": {
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+      "dev": true,
+      "requires": {
+        "cliui": "^5.0.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^2.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^2.0.0",
+        "set-blocking": "^2.0.0",
+        "string-width": "^3.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^13.1.2"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        }
+      }
+    },
+    "yargs-parser": {
+      "version": "13.1.2",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      }
+    },
+    "yargs-unparser": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
+      "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^5.3.1",
+        "decamelize": "^1.2.0",
+        "flat": "^4.1.0",
+        "is-plain-obj": "^1.1.0",
+        "yargs": "^14.2.3"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+          "dev": true
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        },
+        "string-width": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^7.0.1",
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.1.0"
+          }
+        },
+        "yargs": {
+          "version": "14.2.3",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
+          "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
+          "dev": true,
+          "requires": {
+            "cliui": "^5.0.0",
+            "decamelize": "^1.2.0",
+            "find-up": "^3.0.0",
+            "get-caller-file": "^2.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^3.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^15.0.1"
+          }
+        },
+        "yargs-parser": {
+          "version": "15.0.1",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
+          "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
+          "dev": true,
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644 (file)
index 0000000..8afea88
--- /dev/null
@@ -0,0 +1,43 @@
+{
+  "name": "llparse-frontend",
+  "version": "3.0.0",
+  "description": "Frontend for LLParse compiler",
+  "main": "lib/frontend.js",
+  "types": "lib/frontend.d.ts",
+  "scripts": {
+    "build": "tsc",
+    "clean": "rm -rf lib",
+    "prepare": "npm run clean && npm run build",
+    "lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
+    "fix-lint": "npm run lint -- --fix",
+    "mocha": "mocha --timeout=10000 -r ts-node/register/type-check --reporter spec test/*-test.ts",
+    "test": "npm run mocha && npm run lint"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/indutny/llparse-frontend.git"
+  },
+  "keywords": [
+    "llparse",
+    "frontend"
+  ],
+  "author": "Fedor Indutny <fedor@indutny.com> (http://darksi.de/)",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/indutny/llparse-frontend/issues"
+  },
+  "homepage": "https://github.com/indutny/llparse-frontend#readme",
+  "dependencies": {
+    "debug": "^3.2.6",
+    "llparse-builder": "^1.5.2"
+  },
+  "devDependencies": {
+    "@types/debug": "^4.1.5",
+    "@types/mocha": "^8.0.3",
+    "@types/node": "^14.11.8",
+    "mocha": "^8.1.3",
+    "ts-node": "^9.0.0",
+    "tslint": "^5.20.1",
+    "typescript": "^4.0.3"
+  }
+}
diff --git a/src/code/and.ts b/src/code/and.ts
new file mode 100644 (file)
index 0000000..54dc5fd
--- /dev/null
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class And extends FieldValue {
+  constructor(name: string, field: string, value: number) {
+    super('match', `and_${field}_${toCacheKey(value)}`, name, field, value);
+  }
+}
diff --git a/src/code/base.ts b/src/code/base.ts
new file mode 100644 (file)
index 0000000..cde4b6d
--- /dev/null
@@ -0,0 +1,8 @@
+export type Signature = 'match' | 'value' | 'span';
+
+export abstract class Code {
+  constructor(public readonly signature: Signature,
+              public readonly cacheKey: string,
+              public readonly name: string) {
+  }
+}
diff --git a/src/code/external.ts b/src/code/external.ts
new file mode 100644 (file)
index 0000000..f4254c1
--- /dev/null
@@ -0,0 +1,7 @@
+import { Code, Signature } from './base';
+
+export abstract class External extends Code {
+  constructor(signature: Signature, name: string) {
+    super(signature, 'external_' + name, name);
+  }
+}
diff --git a/src/code/field-value.ts b/src/code/field-value.ts
new file mode 100644 (file)
index 0000000..1c7c109
--- /dev/null
@@ -0,0 +1,13 @@
+import * as assert from 'assert';
+
+import { Signature } from './base';
+import { Field } from './field';
+
+export abstract class FieldValue extends Field {
+  constructor(signature: Signature, cacheKey: string, name: string,
+              field: string, public readonly value: number) {
+    super(signature, cacheKey, name, field);
+
+    assert.strictEqual(value, value | 0, 'FieldValue `value` must be integer');
+  }
+}
diff --git a/src/code/field.ts b/src/code/field.ts
new file mode 100644 (file)
index 0000000..c60b8ef
--- /dev/null
@@ -0,0 +1,8 @@
+import { Code, Signature } from './base';
+
+export abstract class Field extends Code {
+  constructor(signature: Signature, cacheKey: string, name: string,
+              public readonly field: string) {
+    super(signature, cacheKey, name);
+  }
+}
diff --git a/src/code/index.ts b/src/code/index.ts
new file mode 100644 (file)
index 0000000..c7d5c69
--- /dev/null
@@ -0,0 +1,15 @@
+export * from './and';
+export * from './base';
+export * from './external';
+export * from './field-value';
+export * from './field';
+export * from './is-equal';
+export * from './load';
+export * from './match';
+export * from './mul-add';
+export * from './or';
+export * from './span';
+export * from './store';
+export * from './test';
+export * from './update';
+export * from './value';
diff --git a/src/code/is-equal.ts b/src/code/is-equal.ts
new file mode 100644 (file)
index 0000000..16a2ee2
--- /dev/null
@@ -0,0 +1,9 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class IsEqual extends FieldValue {
+  constructor(name: string, field: string, value: number) {
+    super('match', `is_equal_${field}_${toCacheKey(value)}`, name, field,
+      value);
+  }
+}
diff --git a/src/code/load.ts b/src/code/load.ts
new file mode 100644 (file)
index 0000000..76b715a
--- /dev/null
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Load extends Field {
+  constructor(name: string, field: string) {
+    super('match', `load_${field}`, name, field);
+  }
+}
diff --git a/src/code/match.ts b/src/code/match.ts
new file mode 100644 (file)
index 0000000..819d2af
--- /dev/null
@@ -0,0 +1,7 @@
+import { External } from './external';
+
+export class Match extends External {
+  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..c99be0d
--- /dev/null
@@ -0,0 +1,26 @@
+import { toCacheKey } from '../utils';
+import { Field } from './field';
+
+export interface IMulAddOptions {
+  readonly base: number;
+  readonly max?: number;
+  readonly signed: boolean;
+}
+
+function toOptionsKey(options: IMulAddOptions): string {
+  let res = `base_${toCacheKey(options.base)}`;
+  if (options.max !== undefined) {
+    res += `_max_${toCacheKey(options.max)}`;
+  }
+  if (options.signed !== undefined) {
+    res += `_signed_${toCacheKey(options.signed)}`;
+  }
+  return res;
+}
+
+export class MulAdd extends Field {
+  constructor(name: string, field: string,
+              public readonly options: IMulAddOptions) {
+    super('value', `mul_add_${field}_${toOptionsKey(options)}`, name, field);
+  }
+}
diff --git a/src/code/or.ts b/src/code/or.ts
new file mode 100644 (file)
index 0000000..2328a9f
--- /dev/null
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class Or extends FieldValue {
+  constructor(name: string, field: string, value: number) {
+    super('match', `or_${field}_${toCacheKey(value)}`, name, field, value);
+  }
+}
diff --git a/src/code/span.ts b/src/code/span.ts
new file mode 100644 (file)
index 0000000..6241e03
--- /dev/null
@@ -0,0 +1,7 @@
+import { External } from './external';
+
+export class Span extends External {
+  constructor(name: string) {
+    super('span', name);
+  }
+}
diff --git a/src/code/store.ts b/src/code/store.ts
new file mode 100644 (file)
index 0000000..c2cb9ea
--- /dev/null
@@ -0,0 +1,7 @@
+import { Field } from './field';
+
+export class Store extends Field {
+  constructor(name: string, field: string) {
+    super('value', `store_${field}`, name, field);
+  }
+}
diff --git a/src/code/test.ts b/src/code/test.ts
new file mode 100644 (file)
index 0000000..21339e9
--- /dev/null
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class Test extends FieldValue {
+  constructor(name: string, field: string, value: number) {
+    super('match', `test_${field}_${toCacheKey(value)}`, name, field, value);
+  }
+}
diff --git a/src/code/update.ts b/src/code/update.ts
new file mode 100644 (file)
index 0000000..5fa5eec
--- /dev/null
@@ -0,0 +1,8 @@
+import { toCacheKey } from '../utils';
+import { FieldValue } from './field-value';
+
+export class Update extends FieldValue {
+  constructor(name: string, field: string, value: number) {
+    super('match', `update_${field}_${toCacheKey(value)}`, name, field, value);
+  }
+}
diff --git a/src/code/value.ts b/src/code/value.ts
new file mode 100644 (file)
index 0000000..4f32ae8
--- /dev/null
@@ -0,0 +1,7 @@
+import { External } from './external';
+
+export class Value extends External {
+  constructor(name: string) {
+    super('value', name);
+  }
+}
diff --git a/src/container/index.ts b/src/container/index.ts
new file mode 100644 (file)
index 0000000..a62aac8
--- /dev/null
@@ -0,0 +1,84 @@
+import * as assert from 'assert';
+
+import { ICodeImplementation } from '../implementation/code';
+import { IImplementation } from '../implementation/full';
+import { INodeImplementation } from '../implementation/node';
+import { ITransformImplementation } from '../implementation/transform';
+import { IWrap } from '../wrap';
+import { ContainerWrap } from './wrap';
+
+export { ContainerWrap };
+
+export class Container {
+  private readonly map: Map<string, IImplementation> = new Map();
+
+  public add(key: string, impl: IImplementation): void {
+    assert(!this.map.has(key), `Duplicate implementation key: "${key}"`);
+    this.map.set(key, impl);
+  }
+
+  public build(): IImplementation {
+    return {
+      code: this.buildCode(),
+      node: this.buildNode(),
+      transform: this.buildTransform(),
+    };
+  }
+
+  public buildCode(): ICodeImplementation {
+    return {
+      And: this.combine((impl) => impl.code.And),
+      IsEqual: this.combine((impl) => impl.code.IsEqual),
+      Load: this.combine((impl) => impl.code.Load),
+      Match: this.combine((impl) => impl.code.Match),
+      MulAdd: this.combine((impl) => impl.code.MulAdd),
+      Or: this.combine((impl) => impl.code.Or),
+      Span: this.combine((impl) => impl.code.Span),
+      Store: this.combine((impl) => impl.code.Store),
+      Test: this.combine((impl) => impl.code.Test),
+      Update: this.combine((impl) => impl.code.Update),
+      Value: this.combine((impl) => impl.code.Value),
+    };
+  }
+
+  public buildNode(): INodeImplementation {
+    return {
+      Consume: this.combine((impl) => impl.node.Consume),
+      Empty: this.combine((impl) => impl.node.Empty),
+      Error: this.combine((impl) => impl.node.Error),
+      Invoke: this.combine((impl) => impl.node.Invoke),
+      Pause: this.combine((impl) => impl.node.Pause),
+      Sequence: this.combine((impl) => impl.node.Sequence),
+      Single: this.combine((impl) => impl.node.Single),
+      SpanEnd: this.combine((impl) => impl.node.SpanEnd),
+      SpanStart: this.combine((impl) => impl.node.SpanStart),
+      TableLookup: this.combine((impl) => impl.node.TableLookup),
+    };
+  }
+
+  public buildTransform(): ITransformImplementation {
+    return {
+      ID: this.combine((impl) => impl.transform.ID),
+      ToLower: this.combine((impl) => impl.transform.ToLower),
+      ToLowerUnsafe: this.combine((impl) => impl.transform.ToLowerUnsafe),
+    };
+  }
+
+  private combine<T>(gather: (impl: IImplementation) => new(n: T) => IWrap<T>)
+    : new(n: T) => ContainerWrap<T> {
+    const wraps: Map<string, new(n: T) => IWrap<T>> = new Map();
+    for (const [ key, impl ] of this.map) {
+      wraps.set(key, gather(impl));
+    }
+
+    return class ContainerWrapSingle extends ContainerWrap<T> {
+      constructor(ref: T) {
+        super(ref);
+
+        for (const [ key, impl ] of wraps) {
+          this.map.set(key, new impl(ref));
+        }
+      }
+    };
+  }
+}
diff --git a/src/container/wrap.ts b/src/container/wrap.ts
new file mode 100644 (file)
index 0000000..f3b886c
--- /dev/null
@@ -0,0 +1,15 @@
+import * as assert from 'assert';
+
+import { IWrap } from '../wrap';
+
+export class ContainerWrap<T> {
+  protected readonly map: Map<string, IWrap<T>> = new Map();
+
+  constructor(public readonly ref: T) {
+  }
+
+  public get<R extends IWrap<T>>(key: string): R {
+    assert(this.map.has(key), `Unknown implementation key "${key}"`);
+    return this.map.get(key)! as R;
+  }
+}
diff --git a/src/enumerator.ts b/src/enumerator.ts
new file mode 100644 (file)
index 0000000..f2940a2
--- /dev/null
@@ -0,0 +1,23 @@
+import { Node } from './node';
+import { IWrap } from './wrap';
+
+export class Enumerator {
+  public getAllNodes(root: IWrap<Node>): ReadonlyArray<IWrap<Node>> {
+    const nodes: Set<IWrap<Node>> = new Set();
+    const queue = [ root ];
+
+    while (queue.length !== 0) {
+      const node = queue.pop()!;
+      for (const slot of node.ref.getSlots()) {
+        if (nodes.has(slot.node)) {
+          continue;
+        }
+
+        nodes.add(slot.node);
+        queue.push(slot.node);
+      }
+    }
+
+    return Array.from(nodes);
+  }
+}
diff --git a/src/frontend.ts b/src/frontend.ts
new file mode 100644 (file)
index 0000000..91c5224
--- /dev/null
@@ -0,0 +1,513 @@
+import * as assert from 'assert';
+import * as debugAPI from 'debug';
+import * as source from 'llparse-builder';
+
+import * as frontend from './namespace/frontend';
+import { Container, ContainerWrap } from './container';
+import { IImplementation } from './implementation';
+import { SpanField } from './span-field';
+import { Trie, TrieEmpty, TrieNode, TrieSequence, TrieSingle } from './trie';
+import { Identifier, IUniqueName } from './utils';
+import { IWrap } from './wrap';
+import { Enumerator } from './enumerator';
+import { Peephole } from './peephole';
+
+const debug = debugAPI('llparse:translator');
+
+export { code, node, transform } from './namespace/frontend';
+
+export {
+  source,
+  Identifier,
+  IUniqueName,
+  IWrap,
+  SpanField,
+  Container,
+  ContainerWrap,
+};
+
+// Minimum number of cases of `single` node to make it eligable for
+// `TableLookup` optimization
+export const DEFAULT_MIN_TABLE_SIZE = 32;
+
+// Maximum width of entry in a table for a `TableLookup` optimization
+export const DEFAULT_MAX_TABLE_WIDTH = 4;
+
+type WrappedNode = IWrap<frontend.node.Node>;
+type WrappedCode = IWrap<frontend.code.Code>;
+
+export interface IFrontendLazyOptions {
+  readonly maxTableElemWidth?: number;
+  readonly minTableSize?: number;
+}
+
+export interface IFrontendResult {
+  readonly prefix: string;
+  readonly properties: ReadonlyArray<source.Property>;
+  readonly root: IWrap<frontend.node.Node>;
+  readonly spans: ReadonlyArray<SpanField>;
+  readonly resumptionTargets: ReadonlySet<WrappedNode>;
+}
+
+interface IFrontendOptions {
+  readonly maxTableElemWidth: number;
+  readonly minTableSize: number;
+}
+
+type MatchChildren = WrappedNode[];
+type MatchResult = WrappedNode | ReadonlyArray<WrappedNode>;
+
+interface ITableLookupTarget {
+  readonly keys: number[];
+  readonly noAdvance: boolean;
+  readonly trie: TrieEmpty;
+}
+
+export class Frontend {
+  private readonly options: IFrontendOptions;
+
+  private readonly id: Identifier = new Identifier(this.prefix + '__n_');
+  private readonly codeId: Identifier = new Identifier(this.prefix + '__c_');
+  private readonly map: Map<source.node.Node, WrappedNode> = new Map();
+  private readonly spanMap: Map<source.Span, SpanField> = new Map();
+  private readonly codeCache: Map<string, WrappedCode> = new Map();
+  private readonly resumptionTargets: Set<WrappedNode> = new Set();
+
+  constructor(private readonly prefix: string,
+              private readonly implementation: IImplementation,
+              options: IFrontendLazyOptions = {}) {
+    this.options = {
+      maxTableElemWidth: options.maxTableElemWidth === undefined ?
+        DEFAULT_MAX_TABLE_WIDTH : options.maxTableElemWidth,
+      minTableSize: options.minTableSize === undefined ?
+        DEFAULT_MIN_TABLE_SIZE : options.minTableSize,
+    };
+
+    assert(0 < this.options.maxTableElemWidth,
+      'Invalid `options.maxTableElemWidth`, must be positive');
+  }
+
+  public compile(root: source.node.Node,
+                 properties: ReadonlyArray<source.Property>): IFrontendResult {
+    debug('checking loops');
+    const lc = new source.LoopChecker();
+    lc.check(root);
+
+    debug('allocating spans');
+    const spanAllocator = new source.SpanAllocator();
+    const sourceSpans = spanAllocator.allocate(root);
+
+    const spans = sourceSpans.concurrency.map((concurrent, index) => {
+      const span = new SpanField(index, concurrent.map((sourceSpan) => {
+        return this.translateSpanCode(sourceSpan.callback);
+      }));
+
+      for (const sourceSpan of concurrent) {
+        this.spanMap.set(sourceSpan, span);
+      }
+
+      return span;
+    });
+
+    debug('translating');
+    let out = this.translate(root);
+
+    debug('enumerating');
+    const enumerator = new Enumerator();
+    let nodes = enumerator.getAllNodes(out);
+
+    debug('peephole optimization');
+    const peephole = new Peephole();
+    out = peephole.optimize(out, nodes);
+
+    debug('re-enumerating');
+    nodes = enumerator.getAllNodes(out);
+
+    debug('registering resumption targets');
+    this.resumptionTargets.add(out);
+    for (const node of nodes) {
+      this.registerNode(node);
+    }
+
+    return {
+      prefix: this.prefix,
+      properties,
+      resumptionTargets: this.resumptionTargets,
+      root: out,
+      spans,
+    };
+  }
+
+  // TODO(indutny): remove this in the next major release
+  public getResumptionTargets(): ReadonlySet<WrappedNode> {
+    return this.resumptionTargets;
+  }
+
+  private translate(node: source.node.Node): WrappedNode {
+    if (this.map.has(node)) {
+      return this.map.get(node)!;
+    }
+
+    const id = () => this.id.id(node.name);
+
+    const nodeImpl = this.implementation.node;
+
+    // Instantiate target class
+    let result: MatchResult;
+    if (node instanceof source.node.Error) {
+      result = new nodeImpl.Error(
+        new frontend.node.Error(id(), node.code, node.reason));
+    } else if (node instanceof source.node.Pause) {
+      result = new nodeImpl.Pause(
+        new frontend.node.Pause(id(), node.code, node.reason));
+    } else if (node instanceof source.node.Consume) {
+      result = new nodeImpl.Consume(
+        new frontend.node.Consume(id(), node.field));
+    } else if (node instanceof source.node.SpanStart) {
+      result = new nodeImpl.SpanStart(
+        new frontend.node.SpanStart(id(), this.spanMap.get(node.span)!,
+          this.translateSpanCode(node.span.callback)));
+    } else if (node instanceof source.node.SpanEnd) {
+      result = new nodeImpl.SpanEnd(
+        new frontend.node.SpanEnd(id(), this.spanMap.get(node.span)!,
+          this.translateSpanCode(node.span.callback)));
+    } else if (node instanceof source.node.Invoke) {
+      assert(node.code.signature === 'match' || node.code.signature === 'value',
+          'Passing `span` callback to `invoke` is not allowed');
+      result = new nodeImpl.Invoke(
+        new frontend.node.Invoke(id(), this.translateCode(node.code)));
+    } else if (node instanceof source.node.Match) {
+      result = this.translateMatch(node);
+    } else {
+      throw new Error(`Unknown node type for "${node.name}" ${node.constructor.toString()}`);
+    }
+
+    // Initialize result
+    const otherwise = node.getOtherwiseEdge();
+
+    if (Array.isArray(result)) {
+      assert(node instanceof source.node.Match);
+      const match = node as source.node.Match;
+
+      // TODO(indutny): move this to llparse-builder?
+      assert.notStrictEqual(otherwise, undefined,
+        `Node "${node.name}" has no \`.otherwise()\``);
+
+      // Assign otherwise to every node of Trie
+      if (otherwise !== undefined) {
+        for (const child of result) {
+          if (!child.ref.otherwise) {
+            child.ref.setOtherwise(this.translate(otherwise.node),
+              otherwise.noAdvance);
+          }
+        }
+      }
+
+      // Assign transform to every node of Trie
+      const transform = this.translateTransform(match.getTransform());
+      for (const child of result) {
+        child.ref.setTransform(transform);
+      }
+
+      assert(result.length >= 1);
+      return result[0];
+    } else {
+      const single: WrappedNode = result as WrappedNode;
+      assert(single.ref instanceof frontend.node.Node);
+
+      // Break loops
+      this.map.set(node, single);
+
+      if (otherwise !== undefined) {
+        single.ref.setOtherwise(this.translate(otherwise.node),
+          otherwise.noAdvance);
+      } else {
+        // TODO(indutny): move this to llparse-builder?
+        assert(node instanceof source.node.Error,
+          `Node "${node.name}" has no \`.otherwise()\``);
+      }
+
+      if (single.ref instanceof frontend.node.Invoke) {
+        for (const edge of node) {
+          single.ref.addEdge(edge.key as number, this.translate(edge.node));
+        }
+      } else {
+        assert.strictEqual(Array.from(node).length, 0);
+      }
+
+      return single;
+    }
+  }
+
+  private registerNode(node: any): void {
+    const nodeImpl = this.implementation.node;
+
+    // Nodes with prologue check (start_pos != end_pos)
+    if (node instanceof nodeImpl.Consume ||
+        node instanceof nodeImpl.Empty ||
+        node instanceof nodeImpl.Sequence ||
+        node instanceof nodeImpl.Single ||
+        node instanceof nodeImpl.SpanStart ||
+        node instanceof nodeImpl.TableLookup) {
+      this.resumptionTargets.add(node);
+
+    // Nodes that can interrupt the execution to be resumed at different node
+    } else if (node instanceof nodeImpl.Pause ||
+               node instanceof nodeImpl.SpanEnd) {
+      this.resumptionTargets.add(node.ref.otherwise!.node);
+    }
+  }
+
+  private translateMatch(node: source.node.Match): MatchResult {
+    const trie = new Trie(node.name);
+
+    const otherwise = node.getOtherwiseEdge();
+    const trieNode = trie.build(Array.from(node));
+    if (trieNode === undefined) {
+      return new this.implementation.node.Empty(
+        new frontend.node.Empty(this.id.id(node.name)));
+    }
+
+    const children: MatchChildren = [];
+    this.translateTrie(node, trieNode, children);
+    assert(children.length >= 1);
+
+    return children;
+  }
+
+  private translateTrie(node: source.node.Match, trie: TrieNode,
+                        children: MatchChildren): WrappedNode {
+    if (trie instanceof TrieEmpty) {
+      assert(this.map.has(node));
+      return this.translate(trie.node);
+    } else if (trie instanceof TrieSingle) {
+      return this.translateSingle(node, trie, children);
+    } else if (trie instanceof TrieSequence) {
+      return this.translateSequence(node, trie, children);
+    } else {
+      throw new Error('Unknown trie node');
+    }
+  }
+
+  private translateSingle(node: source.node.Match, trie: TrieSingle,
+                          children: MatchChildren)
+    : IWrap<frontend.node.Match> {
+    // See if we can apply TableLookup optimization
+    const maybeTable = this.maybeTableLookup(node, trie, children);
+    if (maybeTable !== undefined) {
+      return maybeTable;
+    }
+
+    const single = new this.implementation.node.Single(
+        new frontend.node.Single(this.id.id(node.name)));
+    children.push(single);
+
+    // Break the loop
+    if (!this.map.has(node)) {
+      this.map.set(node, single);
+    }
+    for (const child of trie.children) {
+      const childNode = this.translateTrie(node, child.node, children);
+
+      single.ref.addEdge({
+        key: child.key,
+        noAdvance: child.noAdvance,
+        node: childNode,
+        value: child.node instanceof TrieEmpty ? child.node.value : undefined,
+      });
+    }
+
+    const otherwise = trie.otherwise;
+    if (otherwise) {
+      single.ref.setOtherwise(
+        this.translateTrie(node, otherwise, children),
+        true,
+        otherwise.value);
+    }
+
+    return single;
+  }
+
+  private maybeTableLookup(node: source.node.Match, trie: TrieSingle,
+                           children: MatchChildren)
+    : IWrap<frontend.node.Match> | undefined {
+    if (trie.children.length < this.options.minTableSize) {
+      debug('not enough children of "%s" to allocate table, got %d need %d',
+        node.name, trie.children.length, this.options.minTableSize);
+      return undefined;
+    }
+
+    const targets: Map<source.node.Node, ITableLookupTarget> = new Map();
+
+    const bailout = !trie.children.every((child) => {
+      if (!(child.node instanceof TrieEmpty)) {
+        debug('non-leaf trie child of "%s" prevents table allocation',
+          node.name);
+        return false;
+      }
+
+      const empty: TrieEmpty = child.node;
+
+      // We can't pass values from the table yet
+      if (empty.value !== undefined) {
+        debug('value passing trie leaf of "%s" prevents table allocation',
+          node.name);
+        return false;
+      }
+
+      const target = empty.node;
+      if (!targets.has(target)) {
+        targets.set(target, {
+          keys: [ child.key ],
+          noAdvance: child.noAdvance,
+          trie: empty,
+        });
+        return true;
+      }
+
+      const existing = targets.get(target)!;
+
+      // TODO(indutny): just use it as a sub-key?
+      if (existing.noAdvance !== child.noAdvance) {
+        debug(
+          'noAdvance mismatch in a trie leaf of "%s" prevents ' +
+            'table allocation',
+          node.name);
+        return false;
+      }
+
+      existing.keys.push(child.key);
+      return true;
+    });
+
+    if (bailout) {
+      return undefined;
+    }
+
+    // We've width limit for this optimization
+    if (targets.size >= (1 << this.options.maxTableElemWidth)) {
+      debug('too many different trie targets of "%s" for a table allocation',
+        node.name);
+      return undefined;
+    }
+
+    const table = new this.implementation.node.TableLookup(
+        new frontend.node.TableLookup(this.id.id(node.name)));
+    children.push(table);
+
+    // Break the loop
+    if (!this.map.has(node)) {
+      this.map.set(node, table);
+    }
+
+    targets.forEach((target) => {
+      const next = this.translateTrie(node, target.trie, children);
+
+      table.ref.addEdge({
+        keys: target.keys,
+        noAdvance: target.noAdvance,
+        node: next,
+      });
+    });
+
+    debug('optimized "%s" to a table lookup node', node.name);
+    return table;
+  }
+
+  private translateSequence(node: source.node.Match, trie: TrieSequence,
+                            children: MatchChildren)
+    : IWrap<frontend.node.Match> {
+    const sequence = new this.implementation.node.Sequence(
+        new frontend.node.Sequence(this.id.id(node.name), trie.select));
+    children.push(sequence);
+
+    // Break the loop
+    if (!this.map.has(node)) {
+      this.map.set(node, sequence);
+    }
+
+    const childNode = this.translateTrie(node, trie.child, children);
+
+    const value = trie.child instanceof TrieEmpty ?
+      trie.child.value : undefined;
+
+    sequence.ref.setEdge(childNode, value);
+
+    return sequence;
+  }
+
+  private translateCode(code: source.code.Code): WrappedCode {
+    const prefixed = this.codeId.id(code.name).name;
+    const codeImpl = this.implementation.code;
+
+    let res: WrappedCode;
+    if (code instanceof source.code.IsEqual) {
+      res = new codeImpl.IsEqual(
+        new frontend.code.IsEqual(prefixed, code.field, code.value));
+    } else if (code instanceof source.code.Load) {
+      res = new codeImpl.Load(
+        new frontend.code.Load(prefixed, code.field));
+    } else if (code instanceof source.code.MulAdd) {
+      // TODO(indutny): verify property type
+      const m = new frontend.code.MulAdd(prefixed, code.field, {
+        base: code.options.base,
+        max: code.options.max,
+        signed: code.options.signed === undefined ? true : code.options.signed,
+      });
+      res = new codeImpl.MulAdd(m);
+    } else if (code instanceof source.code.And) {
+      res = new codeImpl.And(
+        new frontend.code.Or(prefixed, code.field, code.value));
+    } else if (code instanceof source.code.Or) {
+      res = new codeImpl.Or(
+        new frontend.code.Or(prefixed, code.field, code.value));
+    } else if (code instanceof source.code.Store) {
+      res = new codeImpl.Store(
+        new frontend.code.Store(prefixed, code.field));
+    } else if (code instanceof source.code.Test) {
+      res = new codeImpl.Test(
+        new frontend.code.Test(prefixed, code.field, code.value));
+    } else if (code instanceof source.code.Update) {
+      res = new codeImpl.Update(
+        new frontend.code.Update(prefixed, code.field, code.value));
+
+    // External callbacks
+    } else if (code instanceof source.code.Span) {
+      res = new codeImpl.Span(new frontend.code.Span(code.name));
+    } else if (code instanceof source.code.Match) {
+      res = new codeImpl.Match(new frontend.code.Match(code.name));
+    } else if (code instanceof source.code.Value) {
+      res = new codeImpl.Value(new frontend.code.Value(code.name));
+    } else {
+      throw new Error(`Unsupported code: "${code.name}"`);
+    }
+
+    // Re-use instances to build them just once
+    if (this.codeCache.has(res.ref.cacheKey)) {
+      return this.codeCache.get(res.ref.cacheKey)!;
+    }
+
+    this.codeCache.set(res.ref.cacheKey, res);
+    return res;
+  }
+
+  private translateSpanCode(code: source.code.Span): IWrap<frontend.code.Span> {
+    return this.translateCode(code) as IWrap<frontend.code.Span>;
+  }
+
+  private translateTransform(transform?: source.transform.Transform)
+    : IWrap<frontend.transform.Transform> {
+    const transformImpl = this.implementation.transform;
+    if (transform === undefined) {
+      return new transformImpl.ID(new frontend.transform.ID());
+    } else if (transform.name === 'to_lower') {
+      return new transformImpl.ToLower(
+        new frontend.transform.ToLower());
+    } else if (transform.name === 'to_lower_unsafe') {
+      return new transformImpl.ToLowerUnsafe(
+        new frontend.transform.ToLowerUnsafe());
+    } else {
+      throw new Error(`Unsupported transform: "${transform.name}"`);
+    }
+  }
+}
diff --git a/src/implementation/code.ts b/src/implementation/code.ts
new file mode 100644 (file)
index 0000000..c467ced
--- /dev/null
@@ -0,0 +1,16 @@
+import * as code from '../code';
+import { IWrap } from '../wrap';
+
+export interface ICodeImplementation {
+  readonly And: new(c: code.And) => IWrap<code.And>;
+  readonly IsEqual: new(c: code.IsEqual) => IWrap<code.IsEqual>;
+  readonly Load: new(c: code.Load) => IWrap<code.Load>;
+  readonly Match: new(c: code.Match) => IWrap<code.Match>;
+  readonly MulAdd: new(c: code.MulAdd) => IWrap<code.MulAdd>;
+  readonly Or: new(c: code.Or) => IWrap<code.Or>;
+  readonly Span: new(c: code.Span) => IWrap<code.Span>;
+  readonly Store: new(c: code.Store) => IWrap<code.Store>;
+  readonly Test: new(c: code.Test) => IWrap<code.Test>;
+  readonly Update: new(c: code.Update) => IWrap<code.Update>;
+  readonly Value: new(c: code.Value) => IWrap<code.Value>;
+}
diff --git a/src/implementation/full.ts b/src/implementation/full.ts
new file mode 100644 (file)
index 0000000..08c4c03
--- /dev/null
@@ -0,0 +1,9 @@
+import { ICodeImplementation } from './code';
+import { INodeImplementation } from './node';
+import { ITransformImplementation } from './transform';
+
+export interface IImplementation {
+  readonly code: ICodeImplementation;
+  readonly node: INodeImplementation;
+  readonly transform: ITransformImplementation;
+}
diff --git a/src/implementation/index.ts b/src/implementation/index.ts
new file mode 100644 (file)
index 0000000..2b5411b
--- /dev/null
@@ -0,0 +1,4 @@
+export * from './code';
+export * from './full';
+export * from './node';
+export * from './transform';
diff --git a/src/implementation/node.ts b/src/implementation/node.ts
new file mode 100644 (file)
index 0000000..af0b3df
--- /dev/null
@@ -0,0 +1,15 @@
+import * as node from '../node';
+import { IWrap } from '../wrap';
+
+export interface INodeImplementation {
+  readonly Consume: new(n: node.Consume) => IWrap<node.Consume>;
+  readonly Empty: new(n: node.Empty) => IWrap<node.Empty>;
+  readonly Error: new(n: node.Error) => IWrap<node.Error>;
+  readonly Invoke: new(n: node.Invoke) => IWrap<node.Invoke>;
+  readonly Pause: new(n: node.Pause) => IWrap<node.Pause>;
+  readonly Sequence: new(n: node.Sequence) => IWrap<node.Sequence>;
+  readonly Single: new(n: node.Single) => IWrap<node.Single>;
+  readonly SpanEnd: new(n: node.SpanEnd) => IWrap<node.SpanEnd>;
+  readonly SpanStart: new(n: node.SpanStart) => IWrap<node.SpanStart>;
+  readonly TableLookup: new(n: node.TableLookup) => IWrap<node.TableLookup>;
+}
diff --git a/src/implementation/transform.ts b/src/implementation/transform.ts
new file mode 100644 (file)
index 0000000..4382284
--- /dev/null
@@ -0,0 +1,9 @@
+import * as transform from '../transform';
+import { IWrap } from '../wrap';
+
+export interface ITransformImplementation {
+  readonly ID: new(t: transform.ID) => IWrap<transform.ID>;
+  readonly ToLower: new(t: transform.ToLower) => IWrap<transform.ToLower>;
+  readonly ToLowerUnsafe: new(t: transform.ToLowerUnsafe)
+    => IWrap<transform.ToLowerUnsafe>;
+}
diff --git a/src/namespace/frontend.ts b/src/namespace/frontend.ts
new file mode 100644 (file)
index 0000000..2f89093
--- /dev/null
@@ -0,0 +1,5 @@
+import * as code from '../code';
+import * as node from '../node';
+import * as transform from '../transform';
+
+export { code, node, transform };
diff --git a/src/node/base.ts b/src/node/base.ts
new file mode 100644 (file)
index 0000000..1e93c49
--- /dev/null
@@ -0,0 +1,46 @@
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Slot } from './slot';
+
+export interface IReadonlyOtherwiseEdge {
+  readonly node: IWrap<Node>;
+  readonly noAdvance: boolean;
+  readonly value: number | undefined;
+}
+
+interface IOtherwiseEdge {
+  node: IWrap<Node>;
+  readonly noAdvance: boolean;
+  readonly value: number | undefined;
+}
+
+export abstract class Node {
+  private privOtherwise: IOtherwiseEdge | undefined;
+  private privSlots: ReadonlyArray<Slot> | undefined;
+
+  constructor(public readonly id: IUniqueName) {
+  }
+
+  public setOtherwise(node: IWrap<Node>, noAdvance: boolean, value?: number) {
+    this.privOtherwise = { node, noAdvance, value };
+  }
+
+  public get otherwise(): IReadonlyOtherwiseEdge | undefined {
+    return this.privOtherwise;
+  }
+
+  public *getSlots() {
+    if (this.privSlots === undefined) {
+      this.privSlots = Array.from(this.buildSlots());
+    }
+
+    yield* this.privSlots;
+  }
+
+  protected *buildSlots() {
+    const otherwise = this.privOtherwise;
+    if (otherwise !== undefined) {
+      yield new Slot(otherwise.node, (value) => otherwise.node = value);
+    }
+  }
+}
diff --git a/src/node/consume.ts b/src/node/consume.ts
new file mode 100644 (file)
index 0000000..6ab49ac
--- /dev/null
@@ -0,0 +1,8 @@
+import { IUniqueName } from '../utils';
+import { Node } from './base';
+
+export class Consume extends Node {
+  constructor(id: IUniqueName, readonly field: string) {
+    super(id);
+  }
+}
diff --git a/src/node/empty.ts b/src/node/empty.ts
new file mode 100644 (file)
index 0000000..45c552c
--- /dev/null
@@ -0,0 +1,4 @@
+import { Node } from './base';
+
+export class Empty extends Node {
+}
diff --git a/src/node/error.ts b/src/node/error.ts
new file mode 100644 (file)
index 0000000..c4e6faf
--- /dev/null
@@ -0,0 +1,9 @@
+import { IUniqueName } from '../utils';
+import { Node } from './base';
+
+export class Error extends Node {
+  constructor(id: IUniqueName, public readonly code: number,
+              public readonly reason: string) {
+    super(id);
+  }
+}
diff --git a/src/node/index.ts b/src/node/index.ts
new file mode 100644 (file)
index 0000000..bd11015
--- /dev/null
@@ -0,0 +1,13 @@
+export * from './base';
+export * from './consume';
+export * from './empty';
+export * from './error';
+export * from './invoke';
+export * from './match';
+export * from './pause';
+export * from './sequence';
+export * from './single';
+export * from './slot';
+export * from './span-end';
+export * from './span-start';
+export * from './table-lookup';
diff --git a/src/node/invoke.ts b/src/node/invoke.ts
new file mode 100644 (file)
index 0000000..ba6ef53
--- /dev/null
@@ -0,0 +1,39 @@
+import { Code } from '../code';
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Slot } from './slot';
+
+interface IInvokeEdge {
+  readonly code: number;
+  node: IWrap<Node>;
+}
+
+export interface IReadonlyInvokeEdge {
+  readonly code: number;
+  readonly node: IWrap<Node>;
+}
+
+export class Invoke extends Node {
+  private readonly privEdges: IInvokeEdge[] = [];
+
+  constructor(id: IUniqueName, public readonly code: IWrap<Code>) {
+    super(id);
+  }
+
+  public addEdge(code: number, node: IWrap<Node>): void {
+    this.privEdges.push({ code, node });
+  }
+
+  public get edges(): ReadonlyArray<IReadonlyInvokeEdge> {
+    return this.privEdges;
+  }
+
+  protected *buildSlots() {
+    for (const edge of this.privEdges) {
+      yield new Slot(edge.node, (value) => edge.node = value);
+    }
+
+    yield* super.buildSlots();
+  }
+}
diff --git a/src/node/match.ts b/src/node/match.ts
new file mode 100644 (file)
index 0000000..8a499d3
--- /dev/null
@@ -0,0 +1,11 @@
+import { Transform } from '../transform';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class Match extends Node {
+  public transform?: IWrap<Transform>;
+
+  public setTransform(transform: IWrap<Transform>): void {
+    this.transform = transform;
+  }
+}
diff --git a/src/node/pause.ts b/src/node/pause.ts
new file mode 100644 (file)
index 0000000..b9923d7
--- /dev/null
@@ -0,0 +1,4 @@
+import { Error as ErrorNode } from './error';
+
+export class Pause extends ErrorNode {
+}
diff --git a/src/node/sequence.ts b/src/node/sequence.ts
new file mode 100644 (file)
index 0000000..c9105b3
--- /dev/null
@@ -0,0 +1,44 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Match } from './match';
+import { Slot } from './slot';
+
+interface ISequenceEdge {
+  node: IWrap<Node>;
+  readonly value: number | undefined;
+}
+
+export interface IReadonlySequenceEdge {
+  readonly node: IWrap<Node>;
+  readonly value: number | undefined;
+}
+
+export class Sequence extends Match {
+  private privEdge?: ISequenceEdge;
+
+  constructor(id: IUniqueName, public readonly select: Buffer) {
+    super(id);
+  }
+
+  public setEdge(node: IWrap<Node>, value?: number | undefined) {
+    assert.strictEqual(this.privEdge, undefined);
+    this.privEdge = { node, value };
+  }
+
+  public get edge(): IReadonlySequenceEdge | undefined {
+    return this.privEdge;
+  }
+
+  protected *buildSlots() {
+    const edge = this.privEdge;
+    if (edge !== undefined) {
+      yield new Slot(edge.node, (value) => edge.node = value);
+    }
+
+    yield* super.buildSlots();
+  }
+}
diff --git a/src/node/single.ts b/src/node/single.ts
new file mode 100644 (file)
index 0000000..0acf715
--- /dev/null
@@ -0,0 +1,46 @@
+import * as assert from 'assert';
+
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Match } from './match';
+import { Slot } from './slot';
+
+interface ISingleEdge {
+  readonly key: number;
+  node: IWrap<Node>;
+  readonly noAdvance: boolean;
+  readonly value: number | undefined;
+}
+
+export interface IReadonlySingleEdge {
+  readonly key: number;
+  node: IWrap<Node>;
+  readonly noAdvance: boolean;
+  readonly value: number | undefined;
+}
+
+export class Single extends Match {
+  private readonly privEdges: ISingleEdge[] = [];
+
+  public addEdge(edge: IReadonlySingleEdge): void {
+    this.privEdges.push({
+      key: edge.key,
+      noAdvance: edge.noAdvance,
+      node: edge.node,
+      value: edge.value,
+    });
+  }
+
+  public get edges(): ReadonlyArray<IReadonlySingleEdge> {
+    return this.privEdges;
+  }
+
+  protected *buildSlots() {
+    for (const edge of this.privEdges) {
+      yield new Slot(edge.node, (value) => edge.node = value);
+    }
+
+    yield* super.buildSlots();
+  }
+}
diff --git a/src/node/slot.ts b/src/node/slot.ts
new file mode 100644 (file)
index 0000000..923da86
--- /dev/null
@@ -0,0 +1,20 @@
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class Slot {
+  private privNode: IWrap<Node>;
+
+  constructor(node: IWrap<Node>,
+              private readonly privUpdate: (value: IWrap<Node>) => void) {
+    this.privNode = node;
+  }
+
+  public get node(): IWrap<Node> {
+    return this.privNode;
+  }
+
+  public set node(value: IWrap<Node>) {
+    this.privNode = value;
+    this.privUpdate(value);
+  }
+}
diff --git a/src/node/span-end.ts b/src/node/span-end.ts
new file mode 100644 (file)
index 0000000..bf8d5cc
--- /dev/null
@@ -0,0 +1,12 @@
+import { Span } from '../code';
+import { SpanField } from '../span-field';
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class SpanEnd extends Node {
+  constructor(id: IUniqueName, public readonly field: SpanField,
+              public readonly callback: IWrap<Span>) {
+    super(id);
+  }
+}
diff --git a/src/node/span-start.ts b/src/node/span-start.ts
new file mode 100644 (file)
index 0000000..89690f1
--- /dev/null
@@ -0,0 +1,12 @@
+import { Span } from '../code';
+import { SpanField } from '../span-field';
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+
+export class SpanStart extends Node {
+  constructor(id: IUniqueName, public readonly field: SpanField,
+              public readonly callback: IWrap<Span>) {
+    super(id);
+  }
+}
diff --git a/src/node/table-lookup.ts b/src/node/table-lookup.ts
new file mode 100644 (file)
index 0000000..9880fc7
--- /dev/null
@@ -0,0 +1,43 @@
+import * as assert from 'assert';
+
+import { IUniqueName } from '../utils';
+import { IWrap } from '../wrap';
+import { Node } from './base';
+import { Match } from './match';
+import { Slot } from './slot';
+
+interface ITableEdge {
+  readonly keys: ReadonlyArray<number>;
+  node: IWrap<Node>;
+  readonly noAdvance: boolean;
+}
+
+export interface IReadonlyTableEdge {
+  readonly keys: ReadonlyArray<number>;
+  readonly node: IWrap<Node>;
+  readonly noAdvance: boolean;
+}
+
+export class TableLookup extends Match {
+  private readonly privEdges: ITableEdge[] = [];
+
+  public addEdge(edge: IReadonlyTableEdge): void {
+    this.privEdges.push({
+      keys: edge.keys,
+      noAdvance: edge.noAdvance,
+      node: edge.node,
+    });
+  }
+
+  public get edges(): ReadonlyArray<IReadonlyTableEdge> {
+    return this.privEdges;
+  }
+
+  protected *buildSlots() {
+    for (const edge of this.privEdges) {
+      yield new Slot(edge.node, (value) => edge.node = value);
+    }
+
+    yield* super.buildSlots();
+  }
+}
diff --git a/src/peephole.ts b/src/peephole.ts
new file mode 100644 (file)
index 0000000..19ac13f
--- /dev/null
@@ -0,0 +1,52 @@
+import { Node, Empty } from './node';
+import { IWrap } from './wrap';
+
+type WrapNode = IWrap<Node>;
+type WrapList = ReadonlyArray<WrapNode>;
+
+export class Peephole {
+  public optimize(root: WrapNode, nodes: WrapList): WrapNode {
+    let changed = new Set(nodes);
+
+    while (changed.size !== 0) {
+      const previous = changed;
+      changed = new Set();
+
+      for (const node of previous) {
+        if (this.optimizeNode(node)) {
+          changed.add(node);
+        }
+      }
+    }
+
+    while (root.ref instanceof Empty) {
+      if (!root.ref.otherwise!.noAdvance) {
+        break;
+      }
+
+      root = root.ref.otherwise!.node;
+    }
+
+    return root;
+  }
+
+  public optimizeNode(node: WrapNode): boolean {
+    let changed = false;
+    for (const slot of node.ref.getSlots()) {
+      if (!(slot.node.ref instanceof Empty)) {
+        continue;
+      }
+
+      const otherwise = slot.node.ref.otherwise!;
+
+      // Node actively skips, can't optimize!
+      if (!otherwise.noAdvance) {
+        continue;
+      }
+
+      slot.node = otherwise.node;
+      changed = true;
+    }
+    return changed;
+  }
+}
diff --git a/src/span-field.ts b/src/span-field.ts
new file mode 100644 (file)
index 0000000..0652f77
--- /dev/null
@@ -0,0 +1,8 @@
+import { Span } from './code';
+import { IWrap } from './wrap';
+
+export class SpanField {
+  constructor(public readonly index: number,
+              public readonly callbacks: ReadonlyArray<IWrap<Span>>) {
+  }
+}
diff --git a/src/transform/base.ts b/src/transform/base.ts
new file mode 100644 (file)
index 0000000..5397326
--- /dev/null
@@ -0,0 +1,4 @@
+export abstract class Transform {
+  constructor(public readonly name: string) {
+  }
+}
diff --git a/src/transform/id.ts b/src/transform/id.ts
new file mode 100644 (file)
index 0000000..d86e3c1
--- /dev/null
@@ -0,0 +1,7 @@
+import { Transform } from './base';
+
+export class ID extends Transform {
+  constructor() {
+    super('id');
+  }
+}
diff --git a/src/transform/index.ts b/src/transform/index.ts
new file mode 100644 (file)
index 0000000..f103b3b
--- /dev/null
@@ -0,0 +1,4 @@
+export * from './base';
+export * from './id';
+export * from './to-lower';
+export * 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/trie/empty.ts b/src/trie/empty.ts
new file mode 100644 (file)
index 0000000..aba52ea
--- /dev/null
@@ -0,0 +1,9 @@
+import { node as api } from 'llparse-builder';
+import { TrieNode } from './node';
+
+export class TrieEmpty extends TrieNode {
+  constructor(public readonly node: api.Node,
+              public readonly value: number | undefined) {
+    super();
+  }
+}
diff --git a/src/trie/index.ts b/src/trie/index.ts
new file mode 100644 (file)
index 0000000..391c6a3
--- /dev/null
@@ -0,0 +1,136 @@
+import * as assert from 'assert';
+import { Buffer } from 'buffer';
+import { Edge, node as api } from 'llparse-builder';
+
+import { TrieEmpty } from './empty';
+import { TrieNode } from './node';
+import { TrieSequence } from './sequence';
+import { ITrieSingleChild, TrieSingle } from './single';
+
+export { TrieEmpty, TrieNode, TrieSequence, TrieSingle };
+
+interface IEdge {
+  readonly key: Buffer;
+  readonly node: api.Node;
+  readonly noAdvance: boolean;
+  readonly value: number | undefined;
+}
+
+type Path = ReadonlyArray<Buffer>;
+type EdgeArray = ReadonlyArray<IEdge>;
+
+export class Trie {
+  constructor(private readonly name: string) {
+  }
+
+  public build(edges: ReadonlyArray<Edge>): undefined | TrieNode {
+    if (edges.length === 0) {
+      return undefined;
+    }
+
+    const internalEdges: IEdge[] = [];
+    for (const edge of edges) {
+      internalEdges.push({
+        key: edge.key as Buffer,
+        noAdvance: edge.noAdvance,
+        node: edge.node,
+        value: edge.value,
+      });
+    }
+
+    return this.level(internalEdges, []);
+  }
+
+  private level(edges: EdgeArray, path: Path): TrieNode {
+    const first = edges[0].key;
+    const last = edges[edges.length - 1].key;
+
+    // Leaf
+    if (edges.length === 1 && edges[0].key.length === 0) {
+      return new TrieEmpty(edges[0].node, edges[0].value);
+    }
+
+    // Find the longest common sub-string
+    let common = 0;
+    for (; common < first.length; common++) {
+      if (first[common] !== last[common]) {
+        break;
+      }
+    }
+
+    // Sequence
+    if (common > 1) {
+      return this.sequence(edges, first.slice(0, common), path);
+    }
+
+    // Single
+    return this.single(edges, path);
+  }
+
+  private slice(edges: EdgeArray, off: number): EdgeArray {
+    return edges.map((edge) => {
+      return {
+        key: edge.key.slice(off),
+        noAdvance: edge.noAdvance,
+        node: edge.node,
+        value: edge.value,
+      };
+    }).sort((a, b) => {
+      return a.key.compare(b.key);
+    });
+  }
+
+  private sequence(edges: EdgeArray, prefix: Buffer, path: Path): TrieNode {
+    const sliced = this.slice(edges, prefix.length);
+    const noAdvance = sliced.some((edge) => edge.noAdvance);
+    assert(!noAdvance);
+    const child = this.level(sliced, path.concat(prefix));
+
+    return new TrieSequence(prefix, child);
+  }
+
+  private single(edges: EdgeArray, path: Path): TrieNode {
+    // Check for duplicates
+    if (edges[0].key.length === 0) {
+      assert(path.length !== 0, `Empty root entry at "${this.name}"`);
+      assert(edges.length === 1 || edges[1].key.length !== 0,
+        `Duplicate entries in "${this.name}" at [ ${path.join(', ')} ]`);
+    }
+
+    let otherwise: TrieEmpty | undefined;
+    const keys: Map<number, IEdge[]> = new Map();
+    for (const edge of edges) {
+      if (edge.key.length === 0) {
+        otherwise = new TrieEmpty(edge.node, edge.value);
+        continue;
+      }
+      const key = edge.key[0];
+
+      if (keys.has(key)) {
+        keys.get(key)!.push(edge);
+      } else {
+        keys.set(key, [ edge ]);
+      }
+    }
+
+    const children: ITrieSingleChild[] = [];
+    keys.forEach((subEdges, key) => {
+      const sliced = this.slice(subEdges, 1);
+      const subpath = path.concat(Buffer.from([ key ]));
+
+      const noAdvance = subEdges.some((edge) => edge.noAdvance);
+      const allSame = subEdges.every((edge) => edge.noAdvance === noAdvance);
+
+      assert(allSame || subEdges.length === 0,
+        'Conflicting `.peek()` and `.match()` entries in ' +
+          `"${this.name}" at [ ${subpath.join(', ')} ]`);
+
+      children.push({
+        key,
+        noAdvance,
+        node: this.level(sliced, subpath),
+      });
+    });
+    return new TrieSingle(children, otherwise);
+  }
+}
diff --git a/src/trie/node.ts b/src/trie/node.ts
new file mode 100644 (file)
index 0000000..31f327c
--- /dev/null
@@ -0,0 +1,2 @@
+export abstract class TrieNode {
+}
diff --git a/src/trie/sequence.ts b/src/trie/sequence.ts
new file mode 100644 (file)
index 0000000..6b17e02
--- /dev/null
@@ -0,0 +1,9 @@
+import { node as api } from 'llparse-builder';
+import { TrieNode } from './node';
+
+export class TrieSequence extends TrieNode {
+  constructor(public readonly select: Buffer,
+              public readonly child: TrieNode) {
+    super();
+  }
+}
diff --git a/src/trie/single.ts b/src/trie/single.ts
new file mode 100644 (file)
index 0000000..c984af0
--- /dev/null
@@ -0,0 +1,16 @@
+import { node as api } from 'llparse-builder';
+import { TrieEmpty } from './empty';
+import { TrieNode } from './node';
+
+export interface ITrieSingleChild {
+  readonly key: number;
+  readonly noAdvance: boolean;
+  readonly node: TrieNode;
+}
+
+export class TrieSingle extends TrieNode {
+  constructor(public readonly children: ReadonlyArray<ITrieSingleChild>,
+              public readonly otherwise: TrieEmpty | undefined) {
+    super();
+  }
+}
diff --git a/src/utils/identifier.ts b/src/utils/identifier.ts
new file mode 100644 (file)
index 0000000..c9ba6ad
--- /dev/null
@@ -0,0 +1,32 @@
+export interface IUniqueName {
+  readonly name: string;
+  readonly originalName: string;
+}
+
+export class Identifier {
+  private readonly ns: Set<string> = new Set();
+
+  constructor(private readonly prefix: string = '',
+              private readonly postfix: string = '') {
+  }
+
+  public id(name: string): IUniqueName {
+    let target = this.prefix + name + this.postfix;
+    if (this.ns.has(target)) {
+      let i = 1;
+      for (; i < this.ns.size; i++) {
+        if (!this.ns.has(target + '_' + i)) {
+          break;
+        }
+      }
+
+      target += '_' + i;
+    }
+
+    this.ns.add(target);
+    return {
+      name: target,
+      originalName: name,
+    };
+  }
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644 (file)
index 0000000..06e86f1
--- /dev/null
@@ -0,0 +1,19 @@
+export { Identifier, IUniqueName } from './identifier';
+
+export function toCacheKey(value: number | boolean): string {
+  if (typeof value === 'number') {
+    if (value < 0) {
+      return 'm' + (-value);
+    } else {
+      return value.toString();
+    }
+  } else if (typeof value === 'boolean') {
+    if (value === true) {
+      return 'true';
+    } else {
+      return 'false';
+    }
+  } else {
+    throw new Error(`Unsupported value: "${value}"`);
+  }
+}
diff --git a/src/wrap.ts b/src/wrap.ts
new file mode 100644 (file)
index 0000000..013adb3
--- /dev/null
@@ -0,0 +1,3 @@
+export interface IWrap<T> {
+  readonly ref: T;
+}
diff --git a/test/container-test.ts b/test/container-test.ts
new file mode 100644 (file)
index 0000000..28b7f1b
--- /dev/null
@@ -0,0 +1,46 @@
+import * as assert from 'assert';
+
+import { Builder } from 'llparse-builder';
+
+import { Container, ContainerWrap, Frontend, node } from '../src/frontend';
+import implementation from './fixtures/a-implementation';
+import { Node } from './fixtures/implementation/node/base';
+
+describe('llparse-frontend/Container', () => {
+  let b: Builder;
+  beforeEach(() => {
+    b = new Builder();
+  });
+
+  it('should translate nodes to implementation', () => {
+    const comb = new Container();
+    comb.add('a', implementation);
+    comb.add('b', implementation);
+
+    const f = new Frontend('llparse', comb.build());
+
+    const root = b.node('root');
+
+    root.match('ab', root);
+    root.match('acd', root);
+    root.match('efg', root);
+    root.otherwise(b.error(123, 'hello'));
+
+    const fRoot = f.compile(root, []).root as ContainerWrap<node.Node>;
+
+    const out: string[] = [];
+    (fRoot.get('a') as Node<node.Node>).build(out);
+
+    assert.deepStrictEqual(out, [
+      '<Single name=llparse__n_root k97=llparse__n_root_1 ' +
+        'k101=llparse__n_root_3 otherwise-no_adv=llparse__n_error/>',
+      '<Single name=llparse__n_root_1 k98=llparse__n_root ' +
+        'k99=llparse__n_root_2 otherwise-no_adv=llparse__n_error/>',
+      '<Single name=llparse__n_root_2 k100=llparse__n_root ' +
+        'otherwise-no_adv=llparse__n_error/>',
+      '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+      '<Sequence name=llparse__n_root_3 select="6667" ' +
+        'otherwise-no_adv=llparse__n_error/>',
+    ]);
+  });
+});
diff --git a/test/fixtures/a-implementation/code/and.ts b/test/fixtures/a-implementation/code/and.ts
new file mode 100644 (file)
index 0000000..c1df821
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class And extends Code<code.And> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/base.ts b/test/fixtures/a-implementation/code/base.ts
new file mode 100644 (file)
index 0000000..d9a7ace
--- /dev/null
@@ -0,0 +1,6 @@
+export abstract class Code<T> {
+  constructor(public readonly ref: T) {
+  }
+
+  public abstract build(): string;
+}
diff --git a/test/fixtures/a-implementation/code/index.ts b/test/fixtures/a-implementation/code/index.ts
new file mode 100644 (file)
index 0000000..855a5cf
--- /dev/null
@@ -0,0 +1,15 @@
+import { And } from './and';
+import { IsEqual } from './is-equal';
+import { Load } from './load';
+import { Match } from './match';
+import { MulAdd } from './mul-add';
+import { Or } from './or';
+import { Span } from './span';
+import { Store } from './store';
+import { Test } from './test';
+import { Update } from './update';
+import { Value } from './value';
+
+export default {
+  And, IsEqual, Load, Match, MulAdd, Or, Span, Store, Test, Update, Value,
+};
diff --git a/test/fixtures/a-implementation/code/is-equal.ts b/test/fixtures/a-implementation/code/is-equal.ts
new file mode 100644 (file)
index 0000000..13a1737
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class IsEqual extends Code<code.IsEqual> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/load.ts b/test/fixtures/a-implementation/code/load.ts
new file mode 100644 (file)
index 0000000..bc97f27
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Load extends Code<code.Load> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/match.ts b/test/fixtures/a-implementation/code/match.ts
new file mode 100644 (file)
index 0000000..e933a71
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Match extends Code<code.Match> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/mul-add.ts b/test/fixtures/a-implementation/code/mul-add.ts
new file mode 100644 (file)
index 0000000..e06a217
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class MulAdd extends Code<code.MulAdd> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/or.ts b/test/fixtures/a-implementation/code/or.ts
new file mode 100644 (file)
index 0000000..a569db4
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Or extends Code<code.Or> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/span.ts b/test/fixtures/a-implementation/code/span.ts
new file mode 100644 (file)
index 0000000..46fc410
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Span extends Code<code.Span> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/store.ts b/test/fixtures/a-implementation/code/store.ts
new file mode 100644 (file)
index 0000000..7a1ca9f
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Store extends Code<code.Store> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/test.ts b/test/fixtures/a-implementation/code/test.ts
new file mode 100644 (file)
index 0000000..4fc8ddb
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Test extends Code<code.Test> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/update.ts b/test/fixtures/a-implementation/code/update.ts
new file mode 100644 (file)
index 0000000..16b20e2
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Update extends Code<code.Update> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/code/value.ts b/test/fixtures/a-implementation/code/value.ts
new file mode 100644 (file)
index 0000000..8e76e2a
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Value extends Code<code.Value> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/index.ts b/test/fixtures/a-implementation/index.ts
new file mode 100644 (file)
index 0000000..1d8d29a
--- /dev/null
@@ -0,0 +1,5 @@
+import code from './code';
+import node from './node';
+import transform from './transform';
+
+export default { code, node, transform };
diff --git a/test/fixtures/a-implementation/node/base.ts b/test/fixtures/a-implementation/node/base.ts
new file mode 100644 (file)
index 0000000..04c8285
--- /dev/null
@@ -0,0 +1,38 @@
+import { ContainerWrap, node } from '../../../../src/frontend';
+
+export abstract class Node<T extends node.Node> {
+  private built: boolean = false;
+
+  constructor(public readonly ref: T) {
+  }
+
+  public build(out: string[]): void {
+    if (this.built) {
+      return;
+    }
+
+    this.built = true;
+    this.doBuild(out);
+
+    if (this.ref.otherwise !== undefined) {
+      const cwrap = this.ref.otherwise.node as ContainerWrap<T>;
+      const otherwise = cwrap.get<Node<T>>('a');
+      otherwise.build(out);
+    }
+  }
+
+  protected format(value: string): string {
+    let otherwise: string = '';
+    if (this.ref.otherwise !== undefined) {
+      const otherwiseRef = this.ref.otherwise.node.ref;
+      otherwise = ' otherwise' +
+        `${this.ref.otherwise.noAdvance ? '-no_adv' : ''}=` +
+        `${otherwiseRef.id.name}`;
+    }
+
+    return `<${this.constructor.name} name=${this.ref.id.name} ` +
+      `${value}${otherwise}/>`;
+  }
+
+  protected abstract doBuild(out: string[]): void;
+}
diff --git a/test/fixtures/a-implementation/node/consume.ts b/test/fixtures/a-implementation/node/consume.ts
new file mode 100644 (file)
index 0000000..cdc6cef
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Consume extends Node<node.Consume> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(`field=${this.ref.field}`));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/empty.ts b/test/fixtures/a-implementation/node/empty.ts
new file mode 100644 (file)
index 0000000..ef1499b
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Empty extends Node<node.Empty> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/error.ts b/test/fixtures/a-implementation/node/error.ts
new file mode 100644 (file)
index 0000000..1a4f31d
--- /dev/null
@@ -0,0 +1,10 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+class ErrorNode extends Node<node.Error> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(`code=${this.ref.code} reason="${this.ref.reason}"`));
+  }
+}
+
+export { ErrorNode as Error };
diff --git a/test/fixtures/a-implementation/node/index.ts b/test/fixtures/a-implementation/node/index.ts
new file mode 100644 (file)
index 0000000..31dbc5e
--- /dev/null
@@ -0,0 +1,15 @@
+import { Consume } from './consume';
+import { Empty } from './empty';
+import { Error } from './error';
+import { Invoke } from './invoke';
+import { Pause } from './pause';
+import { Sequence } from './sequence';
+import { Single } from './single';
+import { SpanEnd } from './span-end';
+import { SpanStart } from './span-start';
+import { TableLookup } from './table-lookup';
+
+export default {
+  Consume, Empty, Error, Invoke, Pause, Sequence, Single, SpanEnd,
+  SpanStart, TableLookup,
+};
diff --git a/test/fixtures/a-implementation/node/invoke.ts b/test/fixtures/a-implementation/node/invoke.ts
new file mode 100644 (file)
index 0000000..674be5f
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Invoke extends Node<node.Invoke> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/pause.ts b/test/fixtures/a-implementation/node/pause.ts
new file mode 100644 (file)
index 0000000..94da63c
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Pause extends Node<node.Pause> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/sequence.ts b/test/fixtures/a-implementation/node/sequence.ts
new file mode 100644 (file)
index 0000000..13fd336
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Sequence extends Node<node.Sequence> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(`select="${this.ref.select.toString('hex')}"`));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/single.ts b/test/fixtures/a-implementation/node/single.ts
new file mode 100644 (file)
index 0000000..d7bcc72
--- /dev/null
@@ -0,0 +1,18 @@
+import { ContainerWrap, node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Single extends Node<node.Single> {
+  protected doBuild(out: string[]): void {
+    const edges: string[] = [];
+    for (const edge of this.ref.edges) {
+      edges.push(`k${edge.key}${edge.noAdvance ? '-no_adv-' : ''}=` +
+        `${edge.node.ref.id.name}`);
+    }
+    out.push(this.format(edges.join(' ')));
+
+    for (const edge of this.ref.edges) {
+      const edgeNode = edge.node as ContainerWrap<node.Node>;
+      edgeNode.get<Node<node.Node>>('a').build(out);
+    }
+  }
+}
diff --git a/test/fixtures/a-implementation/node/span-end.ts b/test/fixtures/a-implementation/node/span-end.ts
new file mode 100644 (file)
index 0000000..dc79b81
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanEnd extends Node<node.SpanEnd> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/span-start.ts b/test/fixtures/a-implementation/node/span-start.ts
new file mode 100644 (file)
index 0000000..32e373c
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanStart extends Node<node.SpanStart> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/a-implementation/node/table-lookup.ts b/test/fixtures/a-implementation/node/table-lookup.ts
new file mode 100644 (file)
index 0000000..e6166d0
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class TableLookup extends Node<node.TableLookup> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/a-implementation/transform/base.ts b/test/fixtures/a-implementation/transform/base.ts
new file mode 100644 (file)
index 0000000..96dc27d
--- /dev/null
@@ -0,0 +1,6 @@
+export abstract class Transform<T> {
+  constructor(public readonly ref: T) {
+  }
+
+  public abstract build(): string;
+}
diff --git a/test/fixtures/a-implementation/transform/id.ts b/test/fixtures/a-implementation/transform/id.ts
new file mode 100644 (file)
index 0000000..e6c1adc
--- /dev/null
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ID extends Transform<transform.ID> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/transform/index.ts b/test/fixtures/a-implementation/transform/index.ts
new file mode 100644 (file)
index 0000000..bed8bc9
--- /dev/null
@@ -0,0 +1,5 @@
+import { ID } from './id';
+import { ToLower } from './to-lower';
+import { ToLowerUnsafe } from './to-lower-unsafe';
+
+export default { ID, ToLower, ToLowerUnsafe };
diff --git a/test/fixtures/a-implementation/transform/to-lower-unsafe.ts b/test/fixtures/a-implementation/transform/to-lower-unsafe.ts
new file mode 100644 (file)
index 0000000..9d175a9
--- /dev/null
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform<transform.ToLowerUnsafe> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/a-implementation/transform/to-lower.ts b/test/fixtures/a-implementation/transform/to-lower.ts
new file mode 100644 (file)
index 0000000..cbe6456
--- /dev/null
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLower extends Transform<transform.ToLower> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/and.ts b/test/fixtures/implementation/code/and.ts
new file mode 100644 (file)
index 0000000..c1df821
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class And extends Code<code.And> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/base.ts b/test/fixtures/implementation/code/base.ts
new file mode 100644 (file)
index 0000000..d9a7ace
--- /dev/null
@@ -0,0 +1,6 @@
+export abstract class Code<T> {
+  constructor(public readonly ref: T) {
+  }
+
+  public abstract build(): string;
+}
diff --git a/test/fixtures/implementation/code/index.ts b/test/fixtures/implementation/code/index.ts
new file mode 100644 (file)
index 0000000..855a5cf
--- /dev/null
@@ -0,0 +1,15 @@
+import { And } from './and';
+import { IsEqual } from './is-equal';
+import { Load } from './load';
+import { Match } from './match';
+import { MulAdd } from './mul-add';
+import { Or } from './or';
+import { Span } from './span';
+import { Store } from './store';
+import { Test } from './test';
+import { Update } from './update';
+import { Value } from './value';
+
+export default {
+  And, IsEqual, Load, Match, MulAdd, Or, Span, Store, Test, Update, Value,
+};
diff --git a/test/fixtures/implementation/code/is-equal.ts b/test/fixtures/implementation/code/is-equal.ts
new file mode 100644 (file)
index 0000000..13a1737
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class IsEqual extends Code<code.IsEqual> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/load.ts b/test/fixtures/implementation/code/load.ts
new file mode 100644 (file)
index 0000000..bc97f27
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Load extends Code<code.Load> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/match.ts b/test/fixtures/implementation/code/match.ts
new file mode 100644 (file)
index 0000000..e933a71
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Match extends Code<code.Match> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/mul-add.ts b/test/fixtures/implementation/code/mul-add.ts
new file mode 100644 (file)
index 0000000..e06a217
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class MulAdd extends Code<code.MulAdd> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/or.ts b/test/fixtures/implementation/code/or.ts
new file mode 100644 (file)
index 0000000..a569db4
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Or extends Code<code.Or> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/span.ts b/test/fixtures/implementation/code/span.ts
new file mode 100644 (file)
index 0000000..46fc410
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Span extends Code<code.Span> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/store.ts b/test/fixtures/implementation/code/store.ts
new file mode 100644 (file)
index 0000000..7a1ca9f
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Store extends Code<code.Store> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/test.ts b/test/fixtures/implementation/code/test.ts
new file mode 100644 (file)
index 0000000..4fc8ddb
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Test extends Code<code.Test> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/update.ts b/test/fixtures/implementation/code/update.ts
new file mode 100644 (file)
index 0000000..16b20e2
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Update extends Code<code.Update> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/code/value.ts b/test/fixtures/implementation/code/value.ts
new file mode 100644 (file)
index 0000000..8e76e2a
--- /dev/null
@@ -0,0 +1,8 @@
+import { code } from '../../../../src/frontend';
+import { Code } from './base';
+
+export class Value extends Code<code.Value> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/index.ts b/test/fixtures/implementation/index.ts
new file mode 100644 (file)
index 0000000..1d8d29a
--- /dev/null
@@ -0,0 +1,5 @@
+import code from './code';
+import node from './node';
+import transform from './transform';
+
+export default { code, node, transform };
diff --git a/test/fixtures/implementation/node/base.ts b/test/fixtures/implementation/node/base.ts
new file mode 100644 (file)
index 0000000..c9fd589
--- /dev/null
@@ -0,0 +1,39 @@
+import { node } from '../../../../src/frontend';
+
+export abstract class Node<T extends node.Node> {
+  private built: boolean = false;
+
+  constructor(public readonly ref: T) {
+  }
+
+  public build(out: string[]): void {
+    if (this.built) {
+      return;
+    }
+
+    this.built = true;
+    this.doBuild(out);
+
+    if (this.ref.otherwise !== undefined) {
+      (this.ref.otherwise.node as Node<T>).build(out);
+    }
+  }
+
+  protected format(value: string): string {
+    let otherwise: string = '';
+    if (this.ref.otherwise !== undefined) {
+      const otherwiseRef = this.ref.otherwise.node.ref;
+      otherwise = ' otherwise' +
+        `${this.ref.otherwise.noAdvance ? '-no_adv' : ''}=` +
+        `${otherwiseRef.id.name}`;
+      if (this.ref.otherwise.value !== undefined) {
+        otherwise += `:${this.ref.otherwise.value}`;
+      }
+    }
+
+    return `<${this.constructor.name} name=${this.ref.id.name} ` +
+      `${value}${otherwise}/>`;
+  }
+
+  protected abstract doBuild(out: string[]): void;
+}
diff --git a/test/fixtures/implementation/node/consume.ts b/test/fixtures/implementation/node/consume.ts
new file mode 100644 (file)
index 0000000..cdc6cef
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Consume extends Node<node.Consume> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(`field=${this.ref.field}`));
+  }
+}
diff --git a/test/fixtures/implementation/node/empty.ts b/test/fixtures/implementation/node/empty.ts
new file mode 100644 (file)
index 0000000..ef1499b
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Empty extends Node<node.Empty> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/implementation/node/error.ts b/test/fixtures/implementation/node/error.ts
new file mode 100644 (file)
index 0000000..1a4f31d
--- /dev/null
@@ -0,0 +1,10 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+class ErrorNode extends Node<node.Error> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(`code=${this.ref.code} reason="${this.ref.reason}"`));
+  }
+}
+
+export { ErrorNode as Error };
diff --git a/test/fixtures/implementation/node/index.ts b/test/fixtures/implementation/node/index.ts
new file mode 100644 (file)
index 0000000..31dbc5e
--- /dev/null
@@ -0,0 +1,15 @@
+import { Consume } from './consume';
+import { Empty } from './empty';
+import { Error } from './error';
+import { Invoke } from './invoke';
+import { Pause } from './pause';
+import { Sequence } from './sequence';
+import { Single } from './single';
+import { SpanEnd } from './span-end';
+import { SpanStart } from './span-start';
+import { TableLookup } from './table-lookup';
+
+export default {
+  Consume, Empty, Error, Invoke, Pause, Sequence, Single, SpanEnd,
+  SpanStart, TableLookup,
+};
diff --git a/test/fixtures/implementation/node/invoke.ts b/test/fixtures/implementation/node/invoke.ts
new file mode 100644 (file)
index 0000000..674be5f
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Invoke extends Node<node.Invoke> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/implementation/node/pause.ts b/test/fixtures/implementation/node/pause.ts
new file mode 100644 (file)
index 0000000..94da63c
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Pause extends Node<node.Pause> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/implementation/node/sequence.ts b/test/fixtures/implementation/node/sequence.ts
new file mode 100644 (file)
index 0000000..bb745f5
--- /dev/null
@@ -0,0 +1,15 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Sequence extends Node<node.Sequence> {
+  protected doBuild(out: string[]): void {
+    let str = `select="${this.ref.select.toString('hex')}" ` +
+        `edge="${this.ref.edge!.node.ref.id.name}"`;
+    if (this.ref.edge!.value !== undefined) {
+      str += `:${this.ref.edge!.value}`;
+    }
+    out.push(this.format(str));
+    const edgeNode = this.ref.edge!.node as Node<node.Node>;
+    edgeNode.build(out);
+  }
+}
diff --git a/test/fixtures/implementation/node/single.ts b/test/fixtures/implementation/node/single.ts
new file mode 100644 (file)
index 0000000..b24ef93
--- /dev/null
@@ -0,0 +1,22 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class Single extends Node<node.Single> {
+  protected doBuild(out: string[]): void {
+    const edges: string[] = [];
+    for (const edge of this.ref.edges) {
+      let str = `k${edge.key}${edge.noAdvance ? '-no_adv-' : ''}=` +
+        `${edge.node.ref.id.name}`;
+      if (edge.value !== undefined) {
+        str += `:${edge.value}`;
+      }
+      edges.push(str);
+    }
+    out.push(this.format(edges.join(' ')));
+
+    for (const edge of this.ref.edges) {
+      const edgeNode = edge.node as Node<node.Node>;
+      edgeNode.build(out);
+    }
+  }
+}
diff --git a/test/fixtures/implementation/node/span-end.ts b/test/fixtures/implementation/node/span-end.ts
new file mode 100644 (file)
index 0000000..dc79b81
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanEnd extends Node<node.SpanEnd> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/implementation/node/span-start.ts b/test/fixtures/implementation/node/span-start.ts
new file mode 100644 (file)
index 0000000..32e373c
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class SpanStart extends Node<node.SpanStart> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/implementation/node/table-lookup.ts b/test/fixtures/implementation/node/table-lookup.ts
new file mode 100644 (file)
index 0000000..e6166d0
--- /dev/null
@@ -0,0 +1,8 @@
+import { node } from '../../../../src/frontend';
+import { Node } from './base';
+
+export class TableLookup extends Node<node.TableLookup> {
+  protected doBuild(out: string[]): void {
+    out.push(this.format(''));
+  }
+}
diff --git a/test/fixtures/implementation/transform/base.ts b/test/fixtures/implementation/transform/base.ts
new file mode 100644 (file)
index 0000000..96dc27d
--- /dev/null
@@ -0,0 +1,6 @@
+export abstract class Transform<T> {
+  constructor(public readonly ref: T) {
+  }
+
+  public abstract build(): string;
+}
diff --git a/test/fixtures/implementation/transform/id.ts b/test/fixtures/implementation/transform/id.ts
new file mode 100644 (file)
index 0000000..e6c1adc
--- /dev/null
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ID extends Transform<transform.ID> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/transform/index.ts b/test/fixtures/implementation/transform/index.ts
new file mode 100644 (file)
index 0000000..bed8bc9
--- /dev/null
@@ -0,0 +1,5 @@
+import { ID } from './id';
+import { ToLower } from './to-lower';
+import { ToLowerUnsafe } from './to-lower-unsafe';
+
+export default { ID, ToLower, ToLowerUnsafe };
diff --git a/test/fixtures/implementation/transform/to-lower-unsafe.ts b/test/fixtures/implementation/transform/to-lower-unsafe.ts
new file mode 100644 (file)
index 0000000..9d175a9
--- /dev/null
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLowerUnsafe extends Transform<transform.ToLowerUnsafe> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/fixtures/implementation/transform/to-lower.ts b/test/fixtures/implementation/transform/to-lower.ts
new file mode 100644 (file)
index 0000000..cbe6456
--- /dev/null
@@ -0,0 +1,8 @@
+import { transform } from '../../../../src/frontend';
+import { Transform } from './base';
+
+export class ToLower extends Transform<transform.ToLower> {
+  public build(): string {
+    return '';
+  }
+}
diff --git a/test/frontend-test.ts b/test/frontend-test.ts
new file mode 100644 (file)
index 0000000..69e075c
--- /dev/null
@@ -0,0 +1,187 @@
+import * as assert from 'assert';
+
+import * as source from 'llparse-builder';
+
+import { Frontend, node } from '../src/frontend';
+import implementation from './fixtures/implementation';
+import { Node } from './fixtures/implementation/node/base';
+
+function checkNodes(f: Frontend, root: source.node.Node,
+                    expected: ReadonlyArray<string>) {
+  const fRoot = f.compile(root, []).root as Node<node.Node>;
+
+  const out: string[] = [];
+  fRoot.build(out);
+
+  assert.deepStrictEqual(out, expected);
+
+  return fRoot;
+}
+
+function checkResumptionTargets(f: Frontend, expected: ReadonlyArray<string>) {
+  const targets = Array.from(f.getResumptionTargets()).map((t) => {
+    return t.ref.id.name;
+  });
+
+  assert.deepStrictEqual(targets, expected);
+}
+
+describe('llparse-frontend', () => {
+  let b: source.Builder;
+  let f: Frontend;
+  beforeEach(() => {
+    b = new source.Builder();
+    f = new Frontend('llparse', implementation);
+  });
+
+  it('should translate nodes to implementation', () => {
+    const root = b.node('root');
+
+    root.match('ab', root);
+    root.match('acd', root);
+    root.match('efg', root);
+    root.otherwise(b.error(123, 'hello'));
+
+    checkNodes(f, root, [
+      '<Single name=llparse__n_root k97=llparse__n_root_1 ' +
+        'k101=llparse__n_root_3 otherwise-no_adv=llparse__n_error/>',
+      '<Single name=llparse__n_root_1 k98=llparse__n_root ' +
+        'k99=llparse__n_root_2 otherwise-no_adv=llparse__n_error/>',
+      '<Single name=llparse__n_root_2 k100=llparse__n_root ' +
+        'otherwise-no_adv=llparse__n_error/>',
+      '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+      '<Sequence name=llparse__n_root_3 select="6667" ' +
+        'edge=\"llparse__n_root\" ' +
+        'otherwise-no_adv=llparse__n_error/>',
+    ]);
+
+    checkResumptionTargets(f, [
+      'llparse__n_root',
+      'llparse__n_root_1',
+      'llparse__n_root_3',
+      'llparse__n_root_2',
+    ]);
+  });
+
+  it('should do peephole optimization', () => {
+    const root = b.node('root');
+    const root1 = b.node('a');
+    const root2 = b.node('b');
+    const node1 = b.node('c');
+    const node2 = b.node('d');
+
+    root.otherwise(root1);
+    root1.otherwise(root2);
+    root2.skipTo(node1);
+    node1.otherwise(node2);
+    node2.otherwise(root);
+
+    checkNodes(f, root, [
+      '<Empty name=llparse__n_b  otherwise=llparse__n_b/>',
+    ]);
+
+    checkResumptionTargets(f, [
+      'llparse__n_b',
+    ]);
+  });
+
+  it('should generate proper resumption targets', () => {
+    b.property('i64', 'counter');
+
+    const root = b.node('root');
+    const end = b.node('end');
+    const store = b.invoke(b.code.store('counter'));
+
+    root.select({ a: 1, b: 2 }, store);
+    root.otherwise(b.error(1, 'okay'));
+
+    store.otherwise(end);
+
+    end.match('ohai', root);
+    end.match('paus', b.pause(1, 'paused').otherwise(
+        b.pause(2, 'paused').otherwise(root)));
+    end.otherwise(b.error(2, 'ohai'));
+
+    checkNodes(f, root, [
+      '<Single name=llparse__n_root k97=llparse__n_invoke_store_counter:1 ' +
+          'k98=llparse__n_invoke_store_counter:2 ' +
+          'otherwise-no_adv=llparse__n_error_1/>',
+      '<Invoke name=llparse__n_invoke_store_counter  ' +
+          'otherwise-no_adv=llparse__n_end/>',
+      '<Single name=llparse__n_end k111=llparse__n_end_1 ' +
+          'k112=llparse__n_end_2 otherwise-no_adv=llparse__n_error/>',
+      '<Sequence name=llparse__n_end_1 select="686169" ' +
+          'edge="llparse__n_root" otherwise-no_adv=llparse__n_error/>',
+      '<ErrorNode name=llparse__n_error code=2 reason="ohai"/>',
+      '<Sequence name=llparse__n_end_2 select="617573" ' +
+          'edge="llparse__n_pause" otherwise-no_adv=llparse__n_error/>',
+      '<Pause name=llparse__n_pause  otherwise-no_adv=llparse__n_pause_1/>',
+      '<Pause name=llparse__n_pause_1  otherwise-no_adv=llparse__n_root/>',
+      '<ErrorNode name=llparse__n_error_1 code=1 reason="okay"/>',
+    ]);
+
+    checkResumptionTargets(f, [
+      'llparse__n_root',
+      'llparse__n_end',
+      'llparse__n_end_1',
+      'llparse__n_end_2',
+      'llparse__n_pause_1',
+    ]);
+  });
+
+  it('should translate Span code into Span', () => {
+    const root = b.invoke(b.code.span('my_span'));
+    root.otherwise(b.error(1, 'okay'));
+
+    const fRoot = checkNodes(f, root, [
+      '<Invoke name=llparse__n_invoke_my_span  ' +
+        'otherwise-no_adv=llparse__n_error/>',
+      '<ErrorNode name=llparse__n_error code=1 reason="okay"/>',
+    ]);
+
+    assert((fRoot.ref as any).code instanceof implementation.code.Span);
+  });
+
+  it('should translate overlapping matches', () => {
+    const root = b.node('root');
+
+    root.match('ab', root);
+    root.match('abc', root);
+    root.otherwise(b.error(123, 'hello'));
+
+    checkNodes(f, root, [
+      '<Sequence name=llparse__n_root select="6162" edge="llparse__n_root_1" otherwise-no_adv=llparse__n_error/>',
+      '<Single name=llparse__n_root_1 k99=llparse__n_root otherwise-no_adv=llparse__n_root/>',
+      '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+    ]);
+
+    checkResumptionTargets(f, [
+      'llparse__n_root',
+      'llparse__n_root_1',
+    ]);
+  });
+
+  it('should translate overlapping matches with values', () => {
+    const root = b.node('root');
+    const store = b.invoke(b.code.store('counter'));
+
+    root.select({
+      ab: 1,
+      abc: 2,
+    }, store);
+    store.otherwise(root);
+    root.otherwise(b.error(123, 'hello'));
+
+    checkNodes(f, root, [
+      '<Sequence name=llparse__n_root select="6162" edge="llparse__n_root_1" otherwise-no_adv=llparse__n_error/>',
+      '<Single name=llparse__n_root_1 k99=llparse__n_invoke_store_counter:2 otherwise-no_adv=llparse__n_invoke_store_counter:1/>',
+      '<Invoke name=llparse__n_invoke_store_counter  otherwise-no_adv=llparse__n_root/>',
+      '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+    ]);
+
+    checkResumptionTargets(f, [
+      'llparse__n_root',
+      'llparse__n_root_1',
+    ]);
+  });
+});
diff --git a/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..24fec09
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "defaultSeverity": "error",
+    "extends": [
+        "tslint:recommended"
+    ],
+    "jsRules": {},
+    "rules": {
+      "no-bitwise": null,
+      "max-line-length": [true, 80],
+      "max-classes-per-file": [true, 1, "exclude-class-expressions"],
+      "quotemark": [
+        true, "single", "avoid-escape", "avoid-template"
+      ]
+    },
+    "rulesDirectory": []
+}