diff --git a/.eslintrc b/.eslintrc index e6dd98c..78ce03a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,11 @@ { "extends": ["eslint:recommended"], + "env": { + "commonjs": true + }, "rules": { + "eqeqeq": "error", + "strict": ["error", "global"], "brace-style": ["warn", "1tbs", {"allowSingleLine": true}], "comma-style": ["warn", "last"], "dot-notation": "warn", @@ -17,6 +22,8 @@ "no-useless-concat": "warn", "no-useless-escape": "warn", "no-with": "error", + "no-floating-decimal": "warn", + "curly": ["warn", "all"], "object-curly-spacing": ["warn", "never"], "one-var": ["warn", "never"], "quote-props": ["warn", "as-needed"], diff --git a/README.md b/README.md index 8003b06..584e168 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,26 @@ Parsimmon is a small library for writing big parsers made up of lots of little p Parsimmon supports IE7 and newer browsers, along with [Node.js][]. It can be used as a standard Node module through [npm][] (named `parsimmon`), or directly in the browser through a script tag, where it exports a global variable called `Parsimmon`. To download the latest browser build, use the [unpkg version][]. For more information on how to use unpkg, see the [unpkg homepage][]. + + ## API Documentation [Full API documentation in `API.md`.][api] @@ -80,7 +100,7 @@ Thanks to [@bd82][] we have a good [benchmark comparing Parsimmon CPU performanc ## Fantasyland -Parsimmon is also compatible with [fantasyland][]. It is a Semigroup, an Applicative Functor, and a Monad. +Parsimmon is also compatible with [fantasyland][]. It implements Semigroup, Apply, Applicative, Functor, Chain, and Monad. [@bd82]: https://github.com/bd82 [@laughinghan]: https://github.com/laughinghan @@ -98,3 +118,5 @@ Parsimmon is also compatible with [fantasyland][]. It is a Semigroup, an Applica [parsec]: https://hackage.haskell.org/package/parsec [fantasyland]: https://github.com/fantasyland/fantasy-land [perf]: https://sap.github.io/chevrotain/performance/ +[es5]: https://kangax.github.io/compat-table/es5/ +[es5-shim]: https://github.com/es-shims/es5-shim diff --git a/examples/json.js b/examples/json.js index 023c617..03cbf6b 100644 --- a/examples/json.js +++ b/examples/json.js @@ -1,3 +1,5 @@ +'use strict'; + // Run me with Node to see my output! let util = require('util'); diff --git a/examples/lisp.js b/examples/lisp.js index c36ba33..06e289a 100644 --- a/examples/lisp.js +++ b/examples/lisp.js @@ -1,7 +1,9 @@ +'use strict'; + // Run me with Node to see my output! -var util = require('util'); -var P = require('../'); +let util = require('util'); +let P = require('../'); /////////////////////////////////////////////////////////////////////// @@ -13,7 +15,7 @@ function spaced(parser) { .skip(P.optWhitespace); } -var Lisp = P.createLanguage({ +let Lisp = P.createLanguage({ Expression: function(r) { return P.alt( r.Symbol, @@ -54,16 +56,16 @@ var Lisp = P.createLanguage({ /////////////////////////////////////////////////////////////////////// -var text = `\ +let text = `\ (list 1 2 (cons 1 (list))) (print 5 golden rings) `; function prettyPrint(x) { - var opts = {depth: null, colors: 'auto'}; - var s = util.inspect(x, opts); + let opts = {depth: null, colors: 'auto'}; + let s = util.inspect(x, opts); console.log(s); } -var ast = Lisp.File.tryParse(text); +let ast = Lisp.File.tryParse(text); prettyPrint(ast); diff --git a/examples/math.js b/examples/math.js index fa5a1a5..7d4df75 100644 --- a/examples/math.js +++ b/examples/math.js @@ -1,3 +1,5 @@ +'use strict'; + // Run me with Node to see my output! let util = require('util'); diff --git a/examples/python-ish.js b/examples/python-ish.js index d9cd121..b1e968e 100644 --- a/examples/python-ish.js +++ b/examples/python-ish.js @@ -1,7 +1,9 @@ +'use strict'; + // Run me with Node to see my output! -var util = require('util'); -var P = require('..'); +let util = require('util'); +let P = require('..'); /////////////////////////////////////////////////////////////////////// @@ -45,7 +47,7 @@ let Pythonish = P.createLanguage({ /////////////////////////////////////////////////////////////////////// -var text = `\ +let text = `\ block: a() b() @@ -62,10 +64,10 @@ block: `; function prettyPrint(x) { - var opts = {depth: null, colors: 'auto'}; - var s = util.inspect(x, opts); + let opts = {depth: null, colors: 'auto'}; + let s = util.inspect(x, opts); console.log(s); } -var ast = Pythonish.Block.tryParse(text); +let ast = Pythonish.Block.tryParse(text); prettyPrint(ast); diff --git a/package-lock.json b/package-lock.json index e952ebe..d207f77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,20 @@ "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=", "dev": true }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dev": true, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, "acorn-jsx": { "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", @@ -50,11 +64,29 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "anymatch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", + "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=", + "dev": true + }, "argparse": { "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", "dev": true }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true + }, + "arr-flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", + "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=", + "dev": true + }, "array-union": { "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", @@ -65,14 +97,39 @@ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, "arrify": { "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "assertion-error": { - "version": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "asn1.js": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", + "dev": true + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true + }, + "async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.4.1.tgz", + "integrity": "sha1-YqVrJ5yYoR0JhwlqAcw+6463u9c=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, "babel-code-frame": { @@ -85,16 +142,112 @@ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true }, + "base64-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", + "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=", + "dev": true + }, + "big.js": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=", + "dev": true + }, + "binary-extensions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz", + "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=", + "dev": true + }, + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", + "dev": true + }, "brace-expansion": { "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", "dev": true }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", + "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=", + "dev": true + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "dev": true + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "dev": true + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true + }, "buffer-shims": { "version": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", "dev": true }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "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 + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, "caller-path": { "version": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", @@ -115,16 +268,23 @@ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true }, - "chai": { - "version": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", - "dev": true - }, "chalk": { "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true + }, + "cipher-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz", + "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=", + "dev": true + }, "circular-json": { "version": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=", @@ -177,16 +337,58 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, "core-util-is": { "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "dev": true + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "dev": true + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "dev": true + }, + "crypto-browserify": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz", + "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=", + "dev": true + }, "d": { "version": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, "debug": { "version": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz", "integrity": "sha1-qfpvvpykPPHnn3O3XAGJy7fW21o=", @@ -197,18 +399,6 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "deep-eql": { - "version": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, - "dependencies": { - "type-detect": { - "version": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } - } - }, "deep-is": { "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", @@ -219,16 +409,64 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true + }, "diff": { "version": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", "dev": true }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "dev": true + }, "doctrine": { "version": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", "dev": true }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "dev": true + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "enhanced-resolve": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz", + "integrity": "sha1-n0tib1dyRe3PSyrYPYbhf09CHew=", + "dev": true + }, + "errno": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", + "dev": true + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true + }, "es5-ext": { "version": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.15.tgz", "integrity": "sha1-wzClk0we4hKEp8CBqG5f2TfJHqY=", @@ -316,11 +554,41 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", + "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=", + "dev": true + }, "exit-hook": { "version": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", "dev": true }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true + }, "fast-levenshtein": { "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", @@ -336,16 +604,703 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true + }, "flat-cache": { "version": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", "dev": true }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true + }, "fs.realpath": { "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", + "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", + "dev": true, + "optional": true, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "dev": true + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "dev": true, + "optional": true + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "optional": true + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.36", + "bundled": true, + "dev": true, + "optional": true + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "dev": true + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "dev": true + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "optional": true + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, "generate-function": { "version": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", @@ -356,11 +1311,29 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "dev": true + }, "glob": { "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", "dev": true }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true + }, "globals": { "version": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz", "integrity": "sha1-DAymltm5u2lNLlRwvTd3fKrVAoY=", @@ -386,6 +1359,48 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true + }, + "hash.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", + "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true + }, + "hosted-git-info": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", + "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=", + "dev": true + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", + "dev": true + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, "ignore": { "version": "https://registry.npmjs.org/ignore/-/ignore-3.3.0.tgz", "integrity": "sha1-OBLSLL6RJfLCtJFXVaG4q9dFoAE=", @@ -396,6 +1411,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, "inflight": { "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", @@ -416,21 +1437,81 @@ "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=", "dev": true }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true + }, "is-buffer": { "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", "dev": true }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, "is-fullwidth-code-point": { "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true + }, "is-my-json-valid": { "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", "dev": true }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true + }, "is-path-cwd": { "version": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", @@ -446,6 +1527,18 @@ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", "dev": true }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, "is-property": { "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", @@ -456,11 +1549,23 @@ "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", "dev": true }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, "isarray": { "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true + }, "jade": { "version": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", @@ -488,11 +1593,23 @@ "integrity": "sha1-M6BexIHIUMiHWSkWb+G+thxyh2Y=", "dev": true }, + "json-loader": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.4.tgz", + "integrity": "sha1-i6oTZaYy9Yo8RtIBdfxgAsluN94=", + "dev": true + }, "json-stable-stringify": { "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, "jsonify": { "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", @@ -513,11 +1630,43 @@ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true + }, "levn": { "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true + }, "lodash": { "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", @@ -533,6 +1682,36 @@ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", "dev": true }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true + }, + "miller-rabin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, "minimatch": { "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", @@ -595,6 +1774,13 @@ "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", "dev": true }, + "nan": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", + "dev": true, + "optional": true + }, "natural-compare": { "version": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", @@ -605,6 +1791,32 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "dev": true }, + "node-libs-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", + "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", + "dev": true, + "dependencies": { + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", + "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=", + "dev": true + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true + }, "number-is-nan": { "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", @@ -615,6 +1827,12 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true + }, "once": { "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", @@ -630,11 +1848,59 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true }, + "os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", + "dev": true + }, "os-homedir": { "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true + }, "path-is-absolute": { "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", @@ -650,6 +1916,18 @@ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.12.tgz", + "integrity": "sha1-vjZ4XFBn6kjYBv+SMojF91C2uKI=", + "dev": true + }, "pify": { "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", @@ -675,6 +1953,18 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "process-nextick-args": { "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", @@ -685,11 +1975,93 @@ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, + "prr": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true + } + } + }, + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true + }, "readable-stream": { "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", "dev": true }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true + }, "readline2": { "version": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", @@ -700,11 +2072,41 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true }, + "regex-cache": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", + "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, "repeat-string": { "version": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, + "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": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, "require-uncached": { "version": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", @@ -735,6 +2137,12 @@ "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", "dev": true }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "dev": true + }, "run-async": { "version": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", @@ -745,6 +2153,42 @@ "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", "dev": true }, + "safe-buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", + "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==", + "dev": true + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "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 + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", + "dev": true + }, "shelljs": { "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", "integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=", @@ -760,16 +2204,52 @@ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, + "source-list-map": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", + "integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE=", + "dev": true + }, "source-map": { "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", "dev": true }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", + "dev": true + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", + "dev": true + }, "sprintf-js": { "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true + }, + "stream-http": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", + "dev": true + }, "string_decoder": { "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz", "integrity": "sha1-8G9BFXtmTYYGn4S9vcmw2KsoFmc=", @@ -817,6 +2297,12 @@ } } }, + "tapable": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.6.tgz", + "integrity": "sha1-IGvo4YiGC1FEJTdebxrom/sB/Y0=", + "dev": true + }, "text-table": { "version": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", @@ -827,6 +2313,18 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "timers-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", + "integrity": "sha1-q0iDz1l9zVCvIRNJoA+8pWrIa4Y=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, "to-iso-string": { "version": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", @@ -837,42 +2335,144 @@ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", "dev": true }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, "type-check": { "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true }, - "type-detect": { - "version": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", - "dev": true - }, "typedarray": { "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "uglify-js": { - "version": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.23.tgz", - "integrity": "sha1-gjDdl4M3EjLWKngh4s+agXJwqKA=", - "dev": true - }, "uglify-to-browserify": { "version": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, "user-home": { "version": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, "util-deprecate": { "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true + }, + "watchpack": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.3.1.tgz", + "integrity": "sha1-fYaTkHsozmAT5/NhCqKhrPB9rYc=", + "dev": true + }, + "webpack": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.6.1.tgz", + "integrity": "sha1-LgRX8KuxrF3zqxBsacZy8jZ4Xwc=", + "dev": true, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true + } + } + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true + } + } + } + } + }, + "webpack-sources": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", + "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=", + "dev": true + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, "window-size": { "version": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", @@ -883,6 +2483,12 @@ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true + }, "wrappy": { "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", @@ -898,10 +2504,25 @@ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, - "yargs": { - "version": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + } + } } } } diff --git a/package.json b/package.json index 5ed42d4..f58d9bd 100644 --- a/package.json +++ b/package.json @@ -17,27 +17,24 @@ "build" ], "main": "src/parsimmon.js", - "browser": "build/parsimmon.browser.min.js", + "browser": "build/parsimmon.umd.min.js", "devDependencies": { - "chai": "^3.5.0", "eslint": "^3.6.0", "mkdirp": "^0.5.1", "mocha": "^2.5.3", "ncp": "^2.0.0", "rimraf": "^2.5.4", - "uglify-js": "2.x" + "webpack": "^2.6.1" }, "license": "MIT", "scripts": { "prebuild": "rimraf build && mkdirp build", - "build": "npm run build:browser && npm run build:browser.min", - "build:browser": "ncp src/parsimmon.js build/parsimmon.browser.js", - "build:browser.min": "uglifyjs --mangle < src/parsimmon.js > build/parsimmon.browser.min.js", + "build": "webpack -p", "prepublishOnly": "npm run test && npm run build", "lint": "eslint .", "lint:fix": "eslint --fix .", "pretest": "npm run lint", - "test": "mocha --ui tdd --reporter dot test/intro.js test/*.test.js", - "watch:test": "mocha --ui tdd --reporter min --watch test/intro.js test/*.test.js" + "test": "mocha --ui tdd --reporter dot test/setup.js test/core/*.test.js test/laws/*.test.js", + "watch:test": "mocha --ui tdd --reporter min --watch test/setup.js test/core/*.test.js test/laws/*.test.js" } } diff --git a/src/parsimmon.js b/src/parsimmon.js index b4117b3..9ba1e01 100644 --- a/src/parsimmon.js +++ b/src/parsimmon.js @@ -1,795 +1,731 @@ -/* global module, define */ - -// This unsightly UMD-module header is here to make this code work without -// modification with CommonJS, AMD, and browser globals. - -(function(root, factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], factory); - } else if (typeof module === 'object' && module.exports) { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); - } else { - // Browser globals (root is window). - root.Parsimmon = factory(); - } -}(this, function() { - 'use strict'; - - // The Parser object is a wrapper for a parser function. - // Externally, you use one to parse a string by calling - // var result = SomeParser.parse('Me Me Me! Parse Me!'); - // You should never call the constructor, rather you should - // construct your Parser from the base parsers and the - // parser combinator methods. - function Parsimmon(action) { - if (!(this instanceof Parsimmon)) { - return new Parsimmon(action); - } - this._ = action; - } +'use strict'; - function isParser(obj) { - return obj instanceof Parsimmon; +function Parsimmon(action) { + if (!(this instanceof Parsimmon)) { + return new Parsimmon(action); } + this._ = action; +} - var _ = Parsimmon.prototype; +var _ = Parsimmon.prototype; - function makeSuccess(index, value) { - return { - status: true, - index: index, - value: value, - furthest: -1, - expected: [] - }; - } +// -*- Helpers -*- - function makeFailure(index, expected) { - return { - status: false, - index: -1, - value: null, - furthest: index, - expected: [expected] - }; - } - - function mergeReplies(result, last) { - if (!last) { - return result; - } - if (result.furthest > last.furthest) { - return result; - } - var expected = (result.furthest === last.furthest) - ? unsafeUnion(result.expected, last.expected) - : last.expected; - return { - status: result.status, - index: result.index, - value: result.value, - furthest: last.furthest, - expected: expected - }; - } +function isParser(obj) { + return obj instanceof Parsimmon; +} - // Returns the sorted set union of two arrays of strings. Note that if both - // arrays are empty, it simply returns the first array, and if exactly one - // array is empty, it returns the other one unsorted. This is safe because - // expectation arrays always start as [] or [x], so as long as we merge with - // this function, we know they stay in sorted order. - function unsafeUnion(xs, ys) { - // Exit early if either array is empty (common case) - var xn = xs.length; - var yn = ys.length; - if (xn === 0) { - return ys; - } else if (yn === 0) { - return xs; - } - // Two non-empty arrays: do the full algorithm - var obj = {}; - for (var i = 0; i < xn; i++) { - obj[xs[i]] = true; - } - for (var j = 0; j < yn; j++) { - obj[ys[j]] = true; - } - var keys = []; - for (var k in obj) { - if (obj.hasOwnProperty(k)) { - keys.push(k); - } +function makeSuccess(index, value) { + return { + status: true, + index: index, + value: value, + furthest: -1, + expected: [] + }; +} + +function makeFailure(index, expected) { + return { + status: false, + index: -1, + value: null, + furthest: index, + expected: [expected] + }; +} + +function mergeReplies(result, last) { + if (!last) { + return result; + } + if (result.furthest > last.furthest) { + return result; + } + var expected = (result.furthest === last.furthest) + ? unsafeUnion(result.expected, last.expected) + : last.expected; + return { + status: result.status, + index: result.index, + value: result.value, + furthest: last.furthest, + expected: expected + }; +} + +function makeLineColumnIndex(input, i) { + var lines = input.slice(0, i).split('\n'); + // Note that unlike the character offset, the line and column offsets are + // 1-based. + var lineWeAreUpTo = lines.length; + var columnWeAreUpTo = lines[lines.length - 1].length + 1; + return { + offset: i, + line: lineWeAreUpTo, + column: columnWeAreUpTo + }; +} + +// Returns the sorted set union of two arrays of strings. Note that if both +// arrays are empty, it simply returns the first array, and if exactly one +// array is empty, it returns the other one unsorted. This is safe because +// expectation arrays always start as [] or [x], so as long as we merge with +// this function, we know they stay in sorted order. +function unsafeUnion(xs, ys) { + // Exit early if either array is empty (common case) + var xn = xs.length; + var yn = ys.length; + if (xn === 0) { + return ys; + } else if (yn === 0) { + return xs; + } + // Two non-empty arrays: do the full algorithm + var obj = {}; + for (var i = 0; i < xn; i++) { + obj[xs[i]] = true; + } + for (var j = 0; j < yn; j++) { + obj[ys[j]] = true; + } + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); } - keys.sort(); - return keys; } + keys.sort(); + return keys; +} - // For ensuring we have the right argument types - function assertParser(p) { - if (!isParser(p)) { - throw new Error('not a parser: ' + p); - } +function assertParser(p) { + if (!isParser(p)) { + throw new Error('not a parser: ' + p); } +} - // TODO: Switch to Array.isArray eventually. - function assertArray(x) { - if ({}.toString.call(x) !== '[object Array]') { - throw new Error('not an array: ' + x); - } +// TODO[ES5]: Switch to Array.isArray eventually. +function assertArray(x) { + if ({}.toString.call(x) !== '[object Array]') { + throw new Error('not an array: ' + x); } +} - function assertNumber(x) { - if (typeof x !== 'number') { - throw new Error('not a number: ' + x); - } +function assertNumber(x) { + if (typeof x !== 'number') { + throw new Error('not a number: ' + x); } +} - function assertRegexp(x) { - if (!(x instanceof RegExp)) { - throw new Error('not a regexp: '+x); - } - var f = flags(x); - for (var i = 0; i < f.length; i++) { - var c = f.charAt(i); - // Only allow regexp flags [imu] for now, since [g] and [y] specifically - // mess up Parsimmon. If more non-stateful regexp flags are added in the - // future, this will need to be revisited. - if (c != 'i' && c != 'm' && c != 'u') { - throw new Error('unsupported regexp flag "' + c + '": ' + x); - } - } +function assertRegexp(x) { + if (!(x instanceof RegExp)) { + throw new Error('not a regexp: '+x); } - - function assertFunction(x) { - if (typeof x !== 'function') { - throw new Error('not a function: ' + x); + var f = flags(x); + for (var i = 0; i < f.length; i++) { + var c = f.charAt(i); + // Only allow regexp flags [imu] for now, since [g] and [y] specifically + // mess up Parsimmon. If more non-stateful regexp flags are added in the + // future, this will need to be revisited. + if (c !== 'i' && c !== 'm' && c !== 'u') { + throw new Error('unsupported regexp flag "' + c + '": ' + x); } } +} - function assertString(x) { - if (typeof x !== 'string') { - throw new Error('not a string: ' + x); - } +function assertFunction(x) { + if (typeof x !== 'function') { + throw new Error('not a function: ' + x); } +} - function formatExpected(expected) { - if (expected.length === 1) { - return expected[0]; - } - return 'one of ' + expected.join(', '); +function assertString(x) { + if (typeof x !== 'string') { + throw new Error('not a string: ' + x); } +} - function formatGot(input, error) { - var index = error.index; - var i = index.offset; - if (i === input.length) { - return ', got the end of the input'; - } - var prefix = (i > 0 ? '\'...' : '\''); - var suffix = (input.length - i > 12 ? '...\'' : '\''); - return ' at line ' + index.line + ' column ' + index.column - + ', got ' + prefix + input.slice(i, i + 12) + suffix; +function formatExpected(expected) { + if (expected.length === 1) { + return expected[0]; } + return 'one of ' + expected.join(', '); +} - function formatError(input, error) { - return 'expected ' + - formatExpected(error.expected) + - formatGot(input, error); +function formatGot(input, error) { + var index = error.index; + var i = index.offset; + if (i === input.length) { + return ', got the end of the input'; } + var prefix = (i > 0 ? '\'...' : '\''); + var suffix = (input.length - i > 12 ? '...\'' : '\''); + return ' at line ' + index.line + ' column ' + index.column + + ', got ' + prefix + input.slice(i, i + 12) + suffix; +} - _.parse = function(input) { - if (typeof input !== 'string') { - throw new Error('.parse must be called with a string as its argument'); - } - var result = this.skip(eof)._(input, 0); +function formatError(input, error) { + return 'expected ' + + formatExpected(error.expected) + + formatGot(input, error); +} - return result.status ? { - status: true, - value: result.value - } : { - status: false, - index: makeLineColumnIndex(input, result.furthest), - expected: result.expected - }; - }; +function flags(re) { + var s = '' + re; + return s.slice(s.lastIndexOf('/') + 1); +} - _.tryParse = function(str) { - var result = this.parse(str); - if (result.status) { - return result.value; - } else { - var msg = formatError(str, result); - var err = new Error(msg); - err.type = 'ParsimmonError'; - err.result = result; - throw err; - } - }; +function anchoredRegexp(re) { + return RegExp('^(?:' + re.source + ')', flags(re)); +} - // [Parsimmon a] -> Parsimmon [a] - function seq() { - var parsers = [].slice.call(arguments); - var numParsers = parsers.length; - for (var j = 0; j < numParsers; j += 1) { - assertParser(parsers[j]); - } - return Parsimmon(function(input, i) { - var result; - var accum = new Array(numParsers); - for (var j = 0; j < numParsers; j += 1) { - result = mergeReplies(parsers[j]._(input, i), result); - if (!result.status) { - return result; - } - accum[j] = result.value; - i = result.index; - } - return mergeReplies(makeSuccess(i, accum), result); - }); - } +// -*- Combinators -*- - function seqMap() { - var args = [].slice.call(arguments); - if (args.length === 0) { - throw new Error('seqMap needs at least one argument'); - } - var mapper = args.pop(); - assertFunction(mapper); - return seq.apply(null, args).map(function(results) { - return mapper.apply(null, results); - }); +function seq() { + var parsers = [].slice.call(arguments); + var numParsers = parsers.length; + for (var j = 0; j < numParsers; j += 1) { + assertParser(parsers[j]); } - - // Revisit this with Object.keys and .bind when we drop ES3 support. - function createLanguage(parsers) { - var language = {}; - for (var key in parsers) { - if ({}.hasOwnProperty.call(parsers, key)) { - (function(key) { - var func = function() { - return parsers[key](language); - }; - language[key] = lazy(func); - }(key)); + return Parsimmon(function(input, i) { + var result; + var accum = new Array(numParsers); + for (var j = 0; j < numParsers; j += 1) { + result = mergeReplies(parsers[j]._(input, i), result); + if (!result.status) { + return result; } + accum[j] = result.value; + i = result.index; } - return language; - } + return mergeReplies(makeSuccess(i, accum), result); + }); +} - /** - * Allows to add custom primitive parsers - */ - function custom(parsingFunction) { - return Parsimmon(parsingFunction(makeSuccess, makeFailure)); +function seqMap() { + var args = [].slice.call(arguments); + if (args.length === 0) { + throw new Error('seqMap needs at least one argument'); } - - function alt() { - var parsers = [].slice.call(arguments); - var numParsers = parsers.length; - if (numParsers === 0) { - return fail('zero alternates'); - } - for (var j = 0; j < numParsers; j += 1) { - assertParser(parsers[j]); + var mapper = args.pop(); + assertFunction(mapper); + return seq.apply(null, args).map(function(results) { + return mapper.apply(null, results); + }); +} + +// TODO[ES5]: Revisit this with Object.keys and .bind. +function createLanguage(parsers) { + var language = {}; + for (var key in parsers) { + if ({}.hasOwnProperty.call(parsers, key)) { + (function(key) { + var func = function() { + return parsers[key](language); + }; + language[key] = lazy(func); + }(key)); } - return Parsimmon(function(input, i) { - var result; - for (var j = 0; j < parsers.length; j += 1) { - result = mergeReplies(parsers[j]._(input, i), result); - if (result.status) return result; - } - return result; - }); } + return language; +} - function sepBy(parser, separator) { - // Argument asserted by sepBy1 - return sepBy1(parser, separator).or(succeed([])); +function alt() { + var parsers = [].slice.call(arguments); + var numParsers = parsers.length; + if (numParsers === 0) { + return fail('zero alternates'); } - - function sepBy1(parser, separator) { - assertParser(parser); - assertParser(separator); - var pairs = separator.then(parser).many(); - return parser.chain(function(r) { - return pairs.map(function(rs) { - return [r].concat(rs); - }); - }); + for (var j = 0; j < numParsers; j += 1) { + assertParser(parsers[j]); } - - // -*- primitive combinators -*- // - _.or = function(alternative) { - return alt(this, alternative); - }; - - _.trim = function(parser) { - return this.wrap(parser, parser); - }; - - _.wrap = function(leftParser, rightParser) { - return seqMap( - leftParser, - this, - rightParser, - function(left, middle) { - return middle; - } - ); - }; - - _.thru = function(wrapper) { - return wrapper(this); - }; - - _.then = function(next) { - if (typeof next === 'function') { - throw new Error('chaining features of .then are no longer supported, use .chain instead'); - } - assertParser(next); - return seq(this, next).map(function(results) { return results[1]; }); - }; - - // -*- optimized iterative combinators -*- // - // equivalent to: - // _.many = function() { - // return this.times(0, Infinity); - // }; - // or, more explicitly: - // _.many = function() { - // var self = this; - // return self.then(function(x) { - // return self.many().then(function(xs) { - // return [x].concat(xs); - // }); - // }).or(succeed([])); - // }; - _.many = function() { - var self = this; - - return Parsimmon(function(input, i) { - var accum = []; - var result = undefined; - - for (;;) { - result = mergeReplies(self._(input, i), result); - if (result.status) { - i = result.index; - accum.push(result.value); - } else { - return mergeReplies(makeSuccess(i, accum), result); - } - } - }); - }; - - _.tie = function() { - return this.map(function(args) { - assertArray(args); - var s = ''; - for (var i = 0; i < args.length; i++) { - assertString(args[i]); - s += args[i]; + return Parsimmon(function(input, i) { + var result; + for (var j = 0; j < parsers.length; j += 1) { + result = mergeReplies(parsers[j]._(input, i), result); + if (result.status) { + return result; } - return s; - }); - }; - - // equivalent to: - // _.times = function(min, max) { - // if (arguments.length < 2) max = min; - // var self = this; - // if (min > 0) { - // return self.then(function(x) { - // return self.times(min - 1, max - 1).then(function(xs) { - // return [x].concat(xs); - // }); - // }); - // } - // else if (max > 0) { - // return self.then(function(x) { - // return self.times(0, max - 1).then(function(xs) { - // return [x].concat(xs); - // }); - // }).or(succeed([])); - // } - // else return succeed([]); - // }; - _.times = function(min, max) { - var self = this; - if (arguments.length < 2) { - max = min; } - assertNumber(min); - assertNumber(max); - return Parsimmon(function(input, i) { - var accum = []; - var result = undefined; - var prevResult = undefined; - for (var times = 0; times < min; times += 1) { - result = self._(input, i); - prevResult = mergeReplies(result, prevResult); - if (result.status) { - i = result.index; - accum.push(result.value); - } else { - return prevResult; - } - } - for (; times < max; times += 1) { - result = self._(input, i); - prevResult = mergeReplies(result, prevResult); - if (result.status) { - i = result.index; - accum.push(result.value); - } else { - break; - } - } - return mergeReplies(makeSuccess(i, accum), prevResult); - }); - }; - - // -*- higher-level combinators -*- // - _.result = function(res) { - return this.map(function() { - return res; - }); - }; - - _.atMost = function(n) { - return this.times(0, n); - }; - - _.atLeast = function(n) { - return seqMap(this.times(n), this.many(), function(init, rest) { - return init.concat(rest); + return result; + }); +} + +function sepBy(parser, separator) { + // Argument asserted by sepBy1 + return sepBy1(parser, separator).or(succeed([])); +} + +function sepBy1(parser, separator) { + assertParser(parser); + assertParser(separator); + var pairs = separator.then(parser).many(); + return parser.chain(function(r) { + return pairs.map(function(rs) { + return [r].concat(rs); }); - }; + }); +} - _.map = function(fn) { - assertFunction(fn); - var self = this; - return Parsimmon(function(input, i) { - var result = self._(input, i); - if (!result.status) { - return result; - } - return mergeReplies(makeSuccess(result.index, fn(result.value)), result); - }); - }; - _['fantasy-land/map'] = _.map; +// -*- Core Parsing Methods -*- - _.skip = function(next) { - return seq(this, next).map(function(results) { return results[0]; }); +_.parse = function(input) { + if (typeof input !== 'string') { + throw new Error('.parse must be called with a string as its argument'); + } + var result = this.skip(eof)._(input, 0); + if (result.status) { + return { + status: true, + value: result.value + }; + } + return { + status: false, + index: makeLineColumnIndex(input, result.furthest), + expected: result.expected }; +}; - _.mark = function() { - return seqMap(index, this, index, function(start, value, end) { - return { - start: start, - value: value, - end: end - }; - }); - }; +// -*- Other Methods -*- - _.node = function(name) { - return seqMap(index, this, index, function(start, value, end) { - return { - name: name, - value: value, - start: start, - end: end - }; - }); - }; +_.tryParse = function(str) { + var result = this.parse(str); + if (result.status) { + return result.value; + } else { + var msg = formatError(str, result); + var err = new Error(msg); + err.type = 'ParsimmonError'; + err.result = result; + throw err; + } +}; + +_.or = function(alternative) { + return alt(this, alternative); +}; + +_.trim = function(parser) { + return this.wrap(parser, parser); +}; + +_.wrap = function(leftParser, rightParser) { + return seqMap( + leftParser, + this, + rightParser, + function(left, middle) { + return middle; + } + ); +}; - _.sepBy = function(separator) { - return sepBy(this, separator); - }; +_.thru = function(wrapper) { + return wrapper(this); +}; - _.sepBy1 = function(separator) { - return sepBy1(this, separator); - }; +_.then = function(next) { + assertParser(next); + return seq(this, next).map(function(results) { return results[1]; }); +}; - _.lookahead = function(x) { - return this.skip(lookahead(x)); - }; +_.many = function() { + var self = this; - _.notFollowedBy = function(x) { - return this.skip(notFollowedBy(x)); - }; + return Parsimmon(function(input, i) { + var accum = []; + var result = undefined; - _.desc = function(expected) { - var self = this; - return Parsimmon(function(input, i) { - var reply = self._(input, i); - if (!reply.status) { - reply.expected = [expected]; + for (;;) { + result = mergeReplies(self._(input, i), result); + if (result.status) { + i = result.index; + accum.push(result.value); + } else { + return mergeReplies(makeSuccess(i, accum), result); } - return reply; - }); - }; - - _.fallback = function(result) { - return this.or(succeed(result)); - }; - - // -*- primitive parsers -*- // - function string(str) { - assertString(str); - var expected = '\'' + str + '\''; - return Parsimmon(function(input, i) { - var j = i + str.length; - var head = input.slice(i, j); - if (head === str) { - return makeSuccess(j, head); + } + }); +}; + +_.tie = function() { + return this.map(function(args) { + assertArray(args); + var s = ''; + for (var i = 0; i < args.length; i++) { + assertString(args[i]); + s += args[i]; + } + return s; + }); +}; + +_.times = function(min, max) { + var self = this; + if (arguments.length < 2) { + max = min; + } + assertNumber(min); + assertNumber(max); + return Parsimmon(function(input, i) { + var accum = []; + var result = undefined; + var prevResult = undefined; + for (var times = 0; times < min; times += 1) { + result = self._(input, i); + prevResult = mergeReplies(result, prevResult); + if (result.status) { + i = result.index; + accum.push(result.value); } else { - return makeFailure(i, expected); + return prevResult; } - }); - } - - function flags(re) { - var s = '' + re; - return s.slice(s.lastIndexOf('/') + 1); - } - - function anchoredRegexp(re) { - return RegExp('^(?:' + re.source + ')', flags(re)); - } - - function regexp(re, group) { - assertRegexp(re); - if (arguments.length >= 2) { - assertNumber(group); - } else { - group = 0; } - var anchored = anchoredRegexp(re); - var expected = '' + re; - return Parsimmon(function(input, i) { - var match = anchored.exec(input.slice(i)); - if (match) { - var fullMatch = match[0]; - var groupMatch = match[group]; - if (groupMatch != null) { - return makeSuccess(i + fullMatch.length, groupMatch); - } + for (; times < max; times += 1) { + result = self._(input, i); + prevResult = mergeReplies(result, prevResult); + if (result.status) { + i = result.index; + accum.push(result.value); + } else { + break; } - return makeFailure(i, expected); - }); - } + } + return mergeReplies(makeSuccess(i, accum), prevResult); + }); +}; - function succeed(value) { - return Parsimmon(function(input, i) { - return makeSuccess(i, value); - }); - } +_.result = function(res) { + return this.map(function() { + return res; + }); +}; - function fail(expected) { - return Parsimmon(function(input, i) { - return makeFailure(i, expected); - }); - } +_.atMost = function(n) { + return this.times(0, n); +}; - function lookahead(x) { - if (isParser(x)) { - return Parsimmon(function(input, i) { - var result = x._(input, i); - result.index = i; - result.value = ''; - return result; - }); - } else if (typeof x === 'string') { - return lookahead(string(x)); - } else if (x instanceof RegExp) { - return lookahead(regexp(x)); +_.atLeast = function(n) { + return seqMap(this.times(n), this.many(), function(init, rest) { + return init.concat(rest); + }); +}; + +_.map = function(fn) { + assertFunction(fn); + var self = this; + return Parsimmon(function(input, i) { + var result = self._(input, i); + if (!result.status) { + return result; } - throw new Error('not a string, regexp, or parser: ' + x); - } + return mergeReplies(makeSuccess(result.index, fn(result.value)), result); + }); +}; - function notFollowedBy(parser) { - assertParser(parser); - return Parsimmon(function(input, i) { - var result = parser._(input, i); - var text = input.slice(i, result.index); - return result.status - ? makeFailure(i, 'not "' + text + '"') - : makeSuccess(i, null); - }); - } +_.skip = function(next) { + return seq(this, next).map(function(results) { return results[0]; }); +}; + +_.mark = function() { + return seqMap(index, this, index, function(start, value, end) { + return { + start: start, + value: value, + end: end + }; + }); +}; - var any = Parsimmon(function(input, i) { - if (i >= input.length) { - return makeFailure(i, 'any character'); +_.node = function(name) { + return seqMap(index, this, index, function(start, value, end) { + return { + name: name, + value: value, + start: start, + end: end + }; + }); +}; + +_.sepBy = function(separator) { + return sepBy(this, separator); +}; + +_.sepBy1 = function(separator) { + return sepBy1(this, separator); +}; + +_.lookahead = function(x) { + return this.skip(lookahead(x)); +}; + +_.notFollowedBy = function(x) { + return this.skip(notFollowedBy(x)); +}; + +_.desc = function(expected) { + var self = this; + return Parsimmon(function(input, i) { + var reply = self._(input, i); + if (!reply.status) { + reply.expected = [expected]; } - return makeSuccess(i+1, input.charAt(i)); + return reply; }); +}; + +_.fallback = function(result) { + return this.or(succeed(result)); +}; - var all = Parsimmon(function(input, i) { - return makeSuccess(input.length, input.slice(i)); +_.ap = function(other) { + return seqMap(other, this, function(f, x) { + return f(x); }); +}; - var eof = Parsimmon(function(input, i) { - if (i < input.length) { - return makeFailure(i, 'EOF'); +_.chain = function(f) { + var self = this; + return Parsimmon(function(input, i) { + var result = self._(input, i); + if (!result.status) { + return result; } - return makeSuccess(i, null); + var nextParser = f(result.value); + return mergeReplies(nextParser._(input, result.index), result); }); +}; + +// -*- Constructors -*- + +function string(str) { + assertString(str); + var expected = '\'' + str + '\''; + return Parsimmon(function(input, i) { + var j = i + str.length; + var head = input.slice(i, j); + if (head === str) { + return makeSuccess(j, head); + } else { + return makeFailure(i, expected); + } + }); +} - function test(predicate) { - assertFunction(predicate); - return Parsimmon(function(input, i) { - var char = input.charAt(i); - if (i < input.length && predicate(char)) { - return makeSuccess(i + 1, char); - } else { - return makeFailure(i, 'a character matching ' + predicate); +function regexp(re, group) { + assertRegexp(re); + if (arguments.length >= 2) { + assertNumber(group); + } else { + group = 0; + } + var anchored = anchoredRegexp(re); + var expected = '' + re; + return Parsimmon(function(input, i) { + var match = anchored.exec(input.slice(i)); + if (match) { + var fullMatch = match[0]; + var groupMatch = match[group]; + if (groupMatch !== null) { + return makeSuccess(i + fullMatch.length, groupMatch); } - }); - } - - function oneOf(str) { - return test(function(ch) { - return str.indexOf(ch) >= 0; - }); - } - - function noneOf(str) { - return test(function(ch) { - return str.indexOf(ch) < 0; - }); - } + } + return makeFailure(i, expected); + }); +} - // TODO: Improve error message using JSON.stringify eventually. - function range(begin, end) { - return test(function(ch) { - return begin <= ch && ch <= end; - }).desc(begin + '-' + end); - } +function succeed(value) { + return Parsimmon(function(input, i) { + return makeSuccess(i, value); + }); +} - function takeWhile(predicate) { - assertFunction(predicate); +function fail(expected) { + return Parsimmon(function(input, i) { + return makeFailure(i, expected); + }); +} +function lookahead(x) { + if (isParser(x)) { return Parsimmon(function(input, i) { - var j = i; - while (j < input.length && predicate(input.charAt(j))) { - j++; - } - return makeSuccess(j, input.slice(i, j)); - }); - } - - function lazy(desc, f) { - if (arguments.length < 2) { - f = desc; - desc = undefined; - } - - var parser = Parsimmon(function(input, i) { - parser._ = f()._; - return parser._(input, i); + var result = x._(input, i); + result.index = i; + result.value = ''; + return result; }); - - if (desc) { - return parser.desc(desc); + } else if (typeof x === 'string') { + return lookahead(string(x)); + } else if (x instanceof RegExp) { + return lookahead(regexp(x)); + } + throw new Error('not a string, regexp, or parser: ' + x); +} + +function notFollowedBy(parser) { + assertParser(parser); + return Parsimmon(function(input, i) { + var result = parser._(input, i); + var text = input.slice(i, result.index); + return result.status + ? makeFailure(i, 'not "' + text + '"') + : makeSuccess(i, null); + }); +} + +function test(predicate) { + assertFunction(predicate); + return Parsimmon(function(input, i) { + var char = input.charAt(i); + if (i < input.length && predicate(char)) { + return makeSuccess(i + 1, char); } else { - return parser; + return makeFailure(i, 'a character matching ' + predicate); } - } + }); +} - function makeLineColumnIndex(input, i) { - var lines = input.slice(0, i).split('\n'); - // Note that unlike the character offset, the line and column offsets are - // 1-based. - var lineWeAreUpTo = lines.length; - var columnWeAreUpTo = lines[lines.length - 1].length + 1; - return { - offset: i, - line: lineWeAreUpTo, - column: columnWeAreUpTo - }; - } +function oneOf(str) { + return test(function(ch) { + return str.indexOf(ch) >= 0; + }); +} - var index = Parsimmon(function(input, i) { - return makeSuccess(i, makeLineColumnIndex(input, i)); +function noneOf(str) { + return test(function(ch) { + return str.indexOf(ch) < 0; }); +} + +function custom(parsingFunction) { + return Parsimmon(parsingFunction(makeSuccess, makeFailure)); +} + +// TODO[ES5]: Improve error message using JSON.stringify eventually. +function range(begin, end) { + return test(function(ch) { + return begin <= ch && ch <= end; + }).desc(begin + '-' + end); +} + +function takeWhile(predicate) { + assertFunction(predicate); + + return Parsimmon(function(input, i) { + var j = i; + while (j < input.length && predicate(input.charAt(j))) { + j++; + } + return makeSuccess(j, input.slice(i, j)); + }); +} - function empty() { - return fail('fantasy-land/empty'); +function lazy(desc, f) { + if (arguments.length < 2) { + f = desc; + desc = undefined; } - // Fantasy Land Semigroup support - _.concat = _.or; - _['fantasy-land/concat'] = _.concat; - - // Fantasy Land Semigroup and Monoid support - _.empty = empty; - _['fantasy-land/empty'] = _.empty; - - // Fantasy Land Applicative support - _.of = succeed; - _['fantasy-land/of'] = _.of; - - // Fantasy Land Applicative support - _.ap = function(other) { - return seqMap(other, this, function(f, x) { - return f(x); - }); - }; - _['fantasy-land/ap'] = _.ap; + var parser = Parsimmon(function(input, i) { + parser._ = f()._; + return parser._(input, i); + }); - // Fantasy Land Monad support - _.chain = function(f) { - var self = this; - return Parsimmon(function(input, i) { - var result = self._(input, i); - if (!result.status) { - return result; - } - var nextParser = f(result.value); - return mergeReplies(nextParser._(input, result.index), result); - }); - }; - _['fantasy-land/chain'] = _.chain; - - var digit = regexp(/[0-9]/).desc('a digit'); - var digits = regexp(/[0-9]*/).desc('optional digits'); - var letter = regexp(/[a-z]/i).desc('a letter'); - var letters = regexp(/[a-z]*/i).desc('optional letters'); - var optWhitespace = regexp(/\s*/).desc('optional whitespace'); - var whitespace = regexp(/\s+/).desc('whitespace'); - - Parsimmon.createLanguage = createLanguage; - Parsimmon.all = all; - Parsimmon.alt = alt; - Parsimmon.any = any; - Parsimmon.custom = custom; - Parsimmon.digit = digit; - Parsimmon.digits = digits; - Parsimmon.eof = eof; - Parsimmon.fail = fail; - Parsimmon.formatError = formatError; - Parsimmon.index = index; - Parsimmon.isParser = isParser; - Parsimmon.lazy = lazy; - Parsimmon.letter = letter; - Parsimmon.letters = letters; - Parsimmon.lookahead = lookahead; - Parsimmon.notFollowedBy = notFollowedBy; - Parsimmon.makeFailure = makeFailure; - Parsimmon.makeSuccess = makeSuccess; - Parsimmon.noneOf = noneOf; - Parsimmon.oneOf = oneOf; - Parsimmon.range = range; - Parsimmon.optWhitespace = optWhitespace; - Parsimmon.Parser = Parsimmon; - Parsimmon.regex = regexp; - Parsimmon.regexp = regexp; - Parsimmon.sepBy = sepBy; - Parsimmon.sepBy1 = sepBy1; - Parsimmon.seq = seq; - Parsimmon.seqMap = seqMap; - Parsimmon.string = string; - Parsimmon.succeed = succeed; - Parsimmon.takeWhile = takeWhile; - Parsimmon.test = test; - Parsimmon.whitespace = whitespace; - - // Fantasy Land Semigroup support - Parsimmon.empty = empty; - Parsimmon['fantasy-land/empty'] = empty; - - // Fantasy Land Applicative support - Parsimmon.of = succeed; - Parsimmon['fantasy-land/of'] = succeed; - - return Parsimmon; -})); + if (desc) { + return parser.desc(desc); + } else { + return parser; + } +} + +// -*- Fantasy Land Extras -*- + +function empty() { + return fail('fantasy-land/empty'); +} + +_.concat = _.or; +_.empty = empty; +_.of = succeed; +_['fantasy-land/ap'] = _.ap; +_['fantasy-land/chain'] = _.chain; +_['fantasy-land/concat'] = _.concat; +_['fantasy-land/empty'] = _.empty; +_['fantasy-land/of'] = _.of; +_['fantasy-land/map'] = _.map; + +// -*- Base Parsers -*- + +var index = Parsimmon(function(input, i) { + return makeSuccess(i, makeLineColumnIndex(input, i)); +}); + +var any = Parsimmon(function(input, i) { + if (i >= input.length) { + return makeFailure(i, 'any character'); + } + return makeSuccess(i + 1, input.charAt(i)); +}); + +var all = Parsimmon(function(input, i) { + return makeSuccess(input.length, input.slice(i)); +}); + +var eof = Parsimmon(function(input, i) { + if (i < input.length) { + return makeFailure(i, 'EOF'); + } + return makeSuccess(i, null); +}); + +var digit = regexp(/[0-9]/).desc('a digit'); +var digits = regexp(/[0-9]*/).desc('optional digits'); +var letter = regexp(/[a-z]/i).desc('a letter'); +var letters = regexp(/[a-z]*/i).desc('optional letters'); +var optWhitespace = regexp(/\s*/).desc('optional whitespace'); +var whitespace = regexp(/\s+/).desc('whitespace'); + +Parsimmon.all = all; +Parsimmon.alt = alt; +Parsimmon.any = any; +Parsimmon.createLanguage = createLanguage; +Parsimmon.custom = custom; +Parsimmon.digit = digit; +Parsimmon.digits = digits; +Parsimmon.empty = empty; +Parsimmon.eof = eof; +Parsimmon.fail = fail; +Parsimmon.formatError = formatError; +Parsimmon.index = index; +Parsimmon.isParser = isParser; +Parsimmon.lazy = lazy; +Parsimmon.letter = letter; +Parsimmon.letters = letters; +Parsimmon.lookahead = lookahead; +Parsimmon.makeFailure = makeFailure; +Parsimmon.makeSuccess = makeSuccess; +Parsimmon.noneOf = noneOf; +Parsimmon.notFollowedBy = notFollowedBy; +Parsimmon.of = succeed; +Parsimmon.oneOf = oneOf; +Parsimmon.optWhitespace = optWhitespace; +Parsimmon.Parser = Parsimmon; +Parsimmon.range = range; +Parsimmon.regex = regexp; +Parsimmon.regexp = regexp; +Parsimmon.sepBy = sepBy; +Parsimmon.sepBy1 = sepBy1; +Parsimmon.seq = seq; +Parsimmon.seqMap = seqMap; +Parsimmon.string = string; +Parsimmon.succeed = succeed; +Parsimmon.takeWhile = takeWhile; +Parsimmon.test = test; +Parsimmon.whitespace = whitespace; +Parsimmon['fantasy-land/empty'] = empty; +Parsimmon['fantasy-land/of'] = succeed; + +module.exports = Parsimmon; diff --git a/test/.eslintrc b/test/.eslintrc index 10d2238..665c8e2 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -1,5 +1,11 @@ { "env": { "node": true + }, + "globals":{ + "Parsimmon": true, + "assert": true, + "suite": true, + "test": true } } diff --git a/test/browser.html b/test/browser.html deleted file mode 100644 index 7cc89f9..0000000 --- a/test/browser.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - Gibbon Tests - - - -
- - - - - - - - - - - diff --git a/test/core/alt.test.js b/test/core/alt.test.js new file mode 100644 index 0000000..8669c0f --- /dev/null +++ b/test/core/alt.test.js @@ -0,0 +1,41 @@ +'use strict'; + +test('Parsimmon.alt', function(){ + var toNode = function(nodeType){ + return function(value) { + return {type: nodeType, value: value}; + }; + }; + + var stringParser = Parsimmon.seq( + Parsimmon.string('"'), + Parsimmon.regexp(/[^"]*/), + Parsimmon.string('"') + ).map(toNode('string')); + + var identifierParser = + Parsimmon.regexp(/[a-zA-Z]*/) + .map(toNode('identifier')); + + var parser = Parsimmon.alt(stringParser, identifierParser); + + assert.deepEqual( + parser.parse('"a string, to be sure"').value, + { + type: 'string', + value: ['"', 'a string, to be sure', '"'] + } + ); + + assert.deepEqual( + parser.parse('anIdentifier').value, + { + type: 'identifier', + value: 'anIdentifier' + } + ); + + assert.throws(function() { + Parsimmon.alt('not a parser'); + }); +}); diff --git a/test/core/atLeast.test.js b/test/core/atLeast.test.js new file mode 100644 index 0000000..a576fb0 --- /dev/null +++ b/test/core/atLeast.test.js @@ -0,0 +1,8 @@ +'use strict'; + +test('atLeast', function() { + var atLeastTwo = Parsimmon.letter.atLeast(2); + assert.deepEqual(atLeastTwo.parse('xy').value, ['x', 'y']); + assert.deepEqual(atLeastTwo.parse('xyzw').value, ['x', 'y', 'z', 'w']); + assert.ok(!atLeastTwo.parse('x').status); +}); diff --git a/test/core/atMost.test.js b/test/core/atMost.test.js new file mode 100644 index 0000000..7b53f8c --- /dev/null +++ b/test/core/atMost.test.js @@ -0,0 +1,9 @@ +'use strict'; + +test('atMost', function() { + var atMostTwo = Parsimmon.letter.atMost(2); + assert.deepEqual(atMostTwo.parse('').value, []); + assert.deepEqual(atMostTwo.parse('a').value, ['a']); + assert.deepEqual(atMostTwo.parse('ab').value, ['a', 'b']); + assert.ok(!atMostTwo.parse('abc').status); +}); diff --git a/test/core/chain.test.js b/test/core/chain.test.js new file mode 100644 index 0000000..ef812ee --- /dev/null +++ b/test/core/chain.test.js @@ -0,0 +1,24 @@ +'use strict'; + +suite('chain', function() { + + test('asserts that a parser is returned', function() { + var parser1 = Parsimmon.letter.chain(function() { return 'not a parser'; }); + assert.throws(function() { parser1.parse('x'); }); + + assert.throws(function() { Parsimmon.letter.then('x'); }); + }); + + test('with a function that returns a parser, continues with that parser', function() { + var piped; + var parser = Parsimmon.string('x').chain(function(x) { + piped = x; + return Parsimmon.string('y'); + }); + + assert.deepEqual(parser.parse('xy'), {status: true, value: 'y'}); + assert.equal(piped, 'x'); + assert.ok(!parser.parse('x').status); + }); + +}); diff --git a/test/core/constructor.test.js b/test/core/constructor.test.js new file mode 100644 index 0000000..4b79b5c --- /dev/null +++ b/test/core/constructor.test.js @@ -0,0 +1,28 @@ +'use strict'; + +test('Parsimmon()', function() { + var good = 'just a Q'; + var bad = 'all I wanted was a Q'; + var justQ = Parsimmon(function(str, i) { + if (str.charAt(i) === 'Q') { + return Parsimmon.makeSuccess(i + 1, good); + } else { + return Parsimmon.makeFailure(i, bad); + } + }); + var result1 = justQ.parse('Q'); + var result2 = justQ.parse('x'); + assert.deepEqual(result1, { + status: true, + value: good, + }); + assert.deepEqual(result2, { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: [bad] + }); +}); diff --git a/test/core/createLanguage.test.js b/test/core/createLanguage.test.js new file mode 100644 index 0000000..716eed3 --- /dev/null +++ b/test/core/createLanguage.test.js @@ -0,0 +1,59 @@ +'use strict'; + +suite('Parsimmon.createLanguage', function() { + + test('should return an object of parsers', function() { + var lang = Parsimmon.createLanguage({ + a: function() { + return Parsimmon.string('a'); + }, + b: function() { + return Parsimmon.string('b'); + } + }); + assert.ok(Parsimmon.isParser(lang.a)); + assert.ok(Parsimmon.isParser(lang.b)); + }); + + test('should allow direct recursion in parsers', function() { + var lang = Parsimmon.createLanguage({ + Parentheses: function(r) { + return Parsimmon.alt( + Parsimmon.string('()'), + Parsimmon.string('(') + .then(r.Parentheses) + .skip(Parsimmon.string(')')) + ); + } + }); + lang.Parentheses.tryParse('(((())))'); + }); + + test('should allow indirect recursion in parsers', function() { + var lang = Parsimmon.createLanguage({ + Value: function(r) { + return Parsimmon.alt( + r.Number, + r.Symbol, + r.List + ); + }, + Number: function() { + return Parsimmon.regexp(/[0-9]+/).map(Number); + }, + Symbol: function() { + return Parsimmon.regexp(/[a-z]+/); + }, + List: function(r) { + return Parsimmon.string('(') + .then(Parsimmon.sepBy(r.Value, r._)) + .skip(Parsimmon.string(')')); + }, + _: function() { + return Parsimmon.optWhitespace; + } + }); + lang.Value.tryParse('(list 1 2 foo (list nice 3 56 989 asdasdas))'); + }); + +}); diff --git a/test/core/custom.test.js b/test/core/custom.test.js new file mode 100644 index 0000000..a65a66c --- /dev/null +++ b/test/core/custom.test.js @@ -0,0 +1,61 @@ +'use strict'; + +suite('Parsimmon.custom', function(){ + test('simple parser definition', function(){ + function customAny() { + return Parsimmon.custom(function(success){ + return function(input, i) { + return success(i+1, input.charAt(i)); + }; + }); + } + + var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']; + var parser = customAny(); + + for (var i = 0; i < letters.length; i++) { + assert.deepEqual(parser.parse(letters[i]), {status: true, value: letters[i]}); + } + }); + + test('failing parser', function(){ + function failer() { + return Parsimmon.custom(function(success, failure){ + return function(input, i) { + return failure(i, 'nothing'); + }; + }); + } + + assert.deepEqual(failer().parse('a'), { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['nothing'] + }); + }); + + test('composes with existing parsers', function(){ + function notChar(char) { + return Parsimmon.custom(function(success, failure) { + return function(input, i) { + if (input.charCodeAt(i) !== char.charCodeAt(0)) { + return success(i+1, input.charAt(i)); + } + return failure(i, 'something different than "' + input.charAt(i)) + '"'; + }; + }); + } + + function join(array) { + return array.join(''); + } + + var parser = Parsimmon.seq(Parsimmon.string('a'), notChar('b').times(5).map(join), notChar('b').or(Parsimmon.string('b'))).map(join); + + assert.deepEqual(parser.parse('acccccb'), {status: true, value: 'acccccb'}); + }); +}); diff --git a/test/core/desc.test.js b/test/core/desc.test.js new file mode 100644 index 0000000..01acd2f --- /dev/null +++ b/test/core/desc.test.js @@ -0,0 +1,60 @@ +'use strict'; + +suite('desc', function() { + + test('allows custom error messages', function() { + var x = Parsimmon.string('x').desc('the letter x'); + var y = Parsimmon.string('y').desc('the letter y'); + var parser = x.then(y); + + assert.deepEqual(parser.parse('a'), { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['the letter x'] + }); + + assert.deepEqual(parser.parse('xa'), { + status: false, + index: { + offset: 1, + line: 1, + column: 2 + }, + expected: ['the letter y'] + }); + }); + + test('allows tagging with `lazy`', function() { + var x = Parsimmon.lazy('the letter x', function() { + return Parsimmon.string('x'); + }); + var y = Parsimmon.lazy('the letter y', function() { + return Parsimmon.string('y'); + }); + var parser = x.then(y); + + assert.deepEqual(parser.parse('a'), { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['the letter x'] + }); + + assert.deepEqual(parser.parse('xa'), { + status: false, + index: { + offset: 1, + line: 1, + column: 2 + }, + expected: ['the letter y'] + }); + }); +}); diff --git a/test/core/empty.test.js b/test/core/empty.test.js new file mode 100644 index 0000000..725df98 --- /dev/null +++ b/test/core/empty.test.js @@ -0,0 +1,11 @@ +'use strict'; + +test('.empty()', function() { + var emptyParse = { + status: false, + expected: ['fantasy-land/empty'], + index: {offset: 0, line: 1, column: 1} + }; + assert.deepEqual(Parsimmon.digit.empty, Parsimmon.empty); + assert.deepEqual(Parsimmon.empty().parse(''), emptyParse); +}); diff --git a/test/core/eof.test.js b/test/core/eof.test.js new file mode 100644 index 0000000..494736c --- /dev/null +++ b/test/core/eof.test.js @@ -0,0 +1,11 @@ +'use strict'; + +test('eof', function() { + var parser = + Parsimmon.optWhitespace + .skip(Parsimmon.eof) + .or(Parsimmon.all.result('default')); + + assert.equal(parser.parse(' ').value, ' '); + assert.equal(parser.parse('x').value, 'default'); +}); diff --git a/test/core/fail.test.js b/test/core/fail.test.js new file mode 100644 index 0000000..bcbea64 --- /dev/null +++ b/test/core/fail.test.js @@ -0,0 +1,62 @@ +'use strict'; + +suite('fail', function() { + var fail = Parsimmon.fail; + var succeed = Parsimmon.succeed; + + test('use Parsimmon.fail to fail dynamically', function() { + var parser = Parsimmon.any.chain(function(ch) { + return fail('a character besides ' + ch); + }).or(Parsimmon.string('x')); + + assert.deepEqual(parser.parse('y'), { + status: false, + index: { + offset: 1, + line: 1, + column: 2 + }, + expected: ['a character besides y'] + }); + assert.equal(parser.parse('x').value, 'x'); + }); + + test('use Parsimmon.succeed or Parsimmon.fail to branch conditionally', function() { + var allowedOperator; + + var parser = + Parsimmon.string('x') + .then(Parsimmon.string('+').or(Parsimmon.string('*'))) + .chain(function(operator) { + if (operator === allowedOperator) { + return succeed(operator); + } + return fail(allowedOperator); + }) + .skip(Parsimmon.string('y')); + + allowedOperator = '+'; + assert.equal(parser.parse('x+y').value, '+'); + assert.deepEqual(parser.parse('x*y'), { + status: false, + index: { + offset: 2, + line: 1, + column: 3 + }, + expected: ['+'] + }); + + allowedOperator = '*'; + assert.equal(parser.parse('x*y').value, '*'); + assert.deepEqual(parser.parse('x+y'), { + status: false, + index: { + offset: 2, + line: 1, + column: 3 + }, + expected: ['*'] + }); + }); +}); diff --git a/test/core/fallback.test.js b/test/core/fallback.test.js new file mode 100644 index 0000000..e22838a --- /dev/null +++ b/test/core/fallback.test.js @@ -0,0 +1,11 @@ +'use strict'; + +suite('fallback', function() { + + test('allows fallback result if no match is found', function() { + var parser = Parsimmon.string('a').fallback('nothing'); + assert.deepEqual(parser.parse('a').value, 'a'); + assert.deepEqual(parser.parse('').value, 'nothing'); + }); + +}); diff --git a/test/core/formatError.test.js b/test/core/formatError.test.js new file mode 100644 index 0000000..9724dc6 --- /dev/null +++ b/test/core/formatError.test.js @@ -0,0 +1,14 @@ +'use strict'; + +test('Parsimmon.formatError', function() { + var parser = + Parsimmon.alt( + Parsimmon.fail('a'), + Parsimmon.fail('b'), + Parsimmon.fail('c') + ); + var expectation = 'expected one of a, b, c, got the end of the input'; + var input = ''; + var answer = Parsimmon.formatError(input, parser.parse(input)); + assert.deepEqual(answer, expectation); +}); diff --git a/test/core/index.test.js b/test/core/index.test.js new file mode 100644 index 0000000..bfd9c45 --- /dev/null +++ b/test/core/index.test.js @@ -0,0 +1,20 @@ +'use strict'; + +test('index', function() { + var parser = Parsimmon.regexp(/^[x\n]*/).then(Parsimmon.index); + assert.deepEqual(parser.parse('').value, { + offset: 0, + line: 1, + column: 1 + }); + assert.deepEqual(parser.parse('xx').value, { + offset: 2, + line: 1, + column: 3 + }); + assert.deepEqual(parser.parse('xx\nxx').value, { + offset: 5, + line: 2, + column: 3 + }); +}); diff --git a/test/core/isParser.test.js b/test/core/isParser.test.js new file mode 100644 index 0000000..63ca77f --- /dev/null +++ b/test/core/isParser.test.js @@ -0,0 +1,9 @@ +'use strict'; + +test('Parsimmon.isParser', function() { + assert.strictEqual(Parsimmon.isParser(undefined), false); + assert.strictEqual(Parsimmon.isParser({}), false); + assert.strictEqual(Parsimmon.isParser(null), false); + assert.strictEqual(Parsimmon.isParser(Parsimmon.string('x')), true); + assert.strictEqual(Parsimmon.isParser(Parsimmon.regexp(/[0-9]/)), true); +}); diff --git a/test/core/lookahead.test.js b/test/core/lookahead.test.js new file mode 100644 index 0000000..13d06c5 --- /dev/null +++ b/test/core/lookahead.test.js @@ -0,0 +1,64 @@ +'use strict'; + +suite('Parsimmon.lookahead', function() { + + test('should handle a string', function() { + Parsimmon.lookahead(''); + }); + + test('should handle a regexp', function() { + Parsimmon.lookahead(/./); + }); + + test('should handle a parser', function() { + Parsimmon.lookahead(Parsimmon.digit); + }); + + test('can be chained as prototype', function() { + var parser = Parsimmon.seq( + Parsimmon.string('abc').lookahead('d'), + Parsimmon.string('d') + ); + var answer = parser.parse('abcd'); + assert.deepEqual(answer.value, ['abc', 'd']); + }); + + test('does not consume from a string', function() { + var parser = Parsimmon.seq( + Parsimmon.string('abc'), + Parsimmon.lookahead('d'), + Parsimmon.string('d') + ); + var answer = parser.parse('abcd'); + assert.deepEqual(answer.value, ['abc', '', 'd']); + }); + + test('does not consume from a regexp', function() { + var parser = Parsimmon.seq( + Parsimmon.string('abc'), + Parsimmon.lookahead(/d/), + Parsimmon.string('d') + ); + var answer = parser.parse('abcd'); + assert.deepEqual(answer.value, ['abc', '', 'd']); + }); + + test('does not consume from a parser', function() { + var weirdParser = Parsimmon.string('Q').or(Parsimmon.string('d')); + var parser = Parsimmon.seq( + Parsimmon.string('abc'), + Parsimmon.lookahead(weirdParser), + Parsimmon.string('d') + ); + var answer = parser.parse('abcd'); + assert.deepEqual(answer.value, ['abc', '', 'd']); + }); + + test('raises error if argument is not a string, regexp, or parser', function() { + assert.throws(function() { Parsimmon.lookahead({}); }); + assert.throws(function() { Parsimmon.lookahead([]); }); + assert.throws(function() { Parsimmon.lookahead(true); }); + assert.throws(function() { Parsimmon.lookahead(12); }); + }); + +}); diff --git a/test/core/makeFailure.test.js b/test/core/makeFailure.test.js new file mode 100644 index 0000000..a13badb --- /dev/null +++ b/test/core/makeFailure.test.js @@ -0,0 +1,14 @@ +'use strict'; + +test('Parsimmon.makeFailure', function() { + var furthest = 4444; + var expected = 'waiting in the clock tower'; + var result = Parsimmon.makeFailure(furthest, expected); + assert.deepEqual(result, { + status: false, + index: -1, + value: null, + furthest: furthest, + expected: [expected] + }); +}); diff --git a/test/core/makeSuccess.test.js b/test/core/makeSuccess.test.js new file mode 100644 index 0000000..138e3b7 --- /dev/null +++ b/test/core/makeSuccess.test.js @@ -0,0 +1,14 @@ +'use strict'; + +test('Parsimmon.makeSuccess', function() { + var index = 11; + var value = 'a lucky number'; + var result = Parsimmon.makeSuccess(index, value); + assert.deepEqual(result, { + status: true, + index: index, + value: value, + furthest: -1, + expected: [] + }); +}); diff --git a/test/core/many.test.js b/test/core/many.test.js new file mode 100644 index 0000000..019c877 --- /dev/null +++ b/test/core/many.test.js @@ -0,0 +1,21 @@ +'use strict'; + +suite('many', function() { + + test('simple case', function() { + var letters = Parsimmon.letter.many(); + assert.deepEqual(letters.parse('x').value, ['x']); + assert.deepEqual(letters.parse('xyz').value, ['x','y','z']); + assert.deepEqual(letters.parse('').value, []); + assert.ok(!letters.parse('1').status); + assert.ok(!letters.parse('xyz1').status); + }); + + test('followed by then', function() { + var parser = Parsimmon.string('x').many().then(Parsimmon.string('y')); + assert.equal(parser.parse('y').value, 'y'); + assert.equal(parser.parse('xy').value, 'y'); + assert.equal(parser.parse('xxxxxy').value, 'y'); + }); + +}); diff --git a/test/core/map.test.js b/test/core/map.test.js new file mode 100644 index 0000000..a46a850 --- /dev/null +++ b/test/core/map.test.js @@ -0,0 +1,26 @@ +'use strict'; + +suite('map', function() { + + test('with a function, pipes the value in and uses that return value', function() { + var piped; + var parser = + Parsimmon.string('x') + .map(function(x) { + piped = x; + return 'y'; + }); + assert.deepEqual( + parser.parse('x'), + {status: true, value: 'y'} + ); + assert.equal(piped, 'x'); + }); + + test('asserts that a function was given', function() { + assert.throws(function() { + Parsimmon.string('x').map('not a function'); + }); + }); + +}); diff --git a/test/core/mark.test.js b/test/core/mark.test.js new file mode 100644 index 0000000..09941fa --- /dev/null +++ b/test/core/mark.test.js @@ -0,0 +1,30 @@ +'use strict'; + +test('mark', function() { + var ys = Parsimmon.regexp(/^y*/).mark(); + var parser = Parsimmon.optWhitespace.then(ys).skip(Parsimmon.optWhitespace); + assert.deepEqual( + parser.parse('').value, + { + value: '', + start: {offset: 0, line: 1, column: 1}, + end: {offset: 0, line: 1, column: 1} + } + ); + assert.deepEqual( + parser.parse(' yy ').value, + { + value: 'yy', + start: {offset: 1, line: 1, column: 2}, + end: {offset: 3, line: 1, column: 4} + } + ); + assert.deepEqual( + parser.parse('\nyy ').value, + { + value: 'yy', + start: {offset: 1, line: 2, column: 1}, + end: {offset: 3, line: 2, column: 3} + } + ); +}); diff --git a/test/core/node.test.js b/test/core/node.test.js new file mode 100644 index 0000000..c46171c --- /dev/null +++ b/test/core/node.test.js @@ -0,0 +1,33 @@ +'use strict'; + +test('.node(name)', function() { + var ys = Parsimmon.regexp(/^y*/).node('Y'); + var parser = ys.trim(Parsimmon.optWhitespace); + assert.deepEqual( + parser.parse('').value, + { + name: 'Y', + value: '', + start: {offset: 0, line: 1, column: 1}, + end: {offset: 0, line: 1, column: 1} + } + ); + assert.deepEqual( + parser.parse(' yy ').value, + { + name: 'Y', + value: 'yy', + start: {offset: 1, line: 1, column: 2}, + end: {offset: 3, line: 1, column: 4} + } + ); + assert.deepEqual( + parser.parse('\nyy ').value, + { + name: 'Y', + value: 'yy', + start: {offset: 1, line: 2, column: 1}, + end: {offset: 3, line: 2, column: 3} + } + ); +}); diff --git a/test/core/notFollowedBy.test.js b/test/core/notFollowedBy.test.js new file mode 100644 index 0000000..c2a08c1 --- /dev/null +++ b/test/core/notFollowedBy.test.js @@ -0,0 +1,36 @@ +'use strict'; + +suite('Parsimmon.notFollowedBy', function() { + + test('fails when its parser argument matches', function() { + var weirdParser = Parsimmon.string('dx'); + var parser = Parsimmon.seq( + Parsimmon.string('abc'), + Parsimmon.notFollowedBy(weirdParser).result('NOT USED'), + Parsimmon.string('dx') + ); + var answer = parser.parse('abcdx'); + assert.deepEqual(answer.expected, ['not "dx"']); + }); + + test('does not consume from its input', function() { + var weirdParser = Parsimmon.string('Q'); + var parser = Parsimmon.seq( + Parsimmon.string('abc'), + Parsimmon.notFollowedBy(weirdParser), + Parsimmon.string('d') + ); + var answer = parser.parse('abcd'); + assert.deepEqual(answer.value, ['abc', null, 'd']); + }); + + test('can be chained from prototype', function() { + var parser = Parsimmon.seq( + Parsimmon.string('abc').notFollowedBy(Parsimmon.string('Q')), + Parsimmon.string('d') + ); + var answer = parser.parse('abcd'); + assert.deepEqual(answer.value, ['abc', 'd']); + }); + +}); diff --git a/test/core/or.test.js b/test/core/or.test.js new file mode 100644 index 0000000..fdb6ad1 --- /dev/null +++ b/test/core/or.test.js @@ -0,0 +1,134 @@ +'use strict'; + +suite('or', function() { + + test('two parsers', function() { + var parser = Parsimmon.string('x').or(Parsimmon.string('y')); + assert.equal(parser.parse('x').value, 'x'); + assert.equal(parser.parse('y').value, 'y'); + assert.ok(!parser.parse('z').status); + }); + + test('with chain', function() { + var parser = Parsimmon.string('\\') + .chain(function() { + return Parsimmon.string('y'); + }).or(Parsimmon.string('z')); + assert.equal(parser.parse('\\y').value, 'y'); + assert.equal(parser.parse('z').value, 'z'); + assert.ok(!parser.parse('\\z').status); + }); + + test('asserts that a parser was given', function() { + assert.throws(function() { Parsimmon.string('x').or('not a parser'); }); + }); + + test('prefer longest branch', function() { + var parser = + Parsimmon.string('abc') + .then(Parsimmon.string('def')) + .or(Parsimmon.string('ab').then(Parsimmon.string('cd'))); + + assert.deepEqual(parser.parse('abc'), { + status: false, + index: { + offset: 3, + line: 1, + column: 4 + }, + expected: ['\'def\''] + }); + }); + + test('prefer last of equal length branches', function() { + var parser = + Parsimmon.string('abc') + .then(Parsimmon.string('def')) + .or(Parsimmon.string('abc').then(Parsimmon.string('d'))); + + assert.deepEqual(parser.parse('abc'), { + status: false, + index: { + offset: 3, + line: 1, + column: 4 + }, + expected: ['\'d\'', '\'def\''] + }); + }); + + test('prefer longest branch even after a success', function() { + var parser = + Parsimmon.string('abcdef') + .then(Parsimmon.string('g')) + .or(Parsimmon.string('ab')) + .then(Parsimmon.string('cd')).then(Parsimmon.string('xyz')); + + assert.deepEqual(parser.parse('abcdef'), { + status: false, + index: { + offset: 6, + line: 1, + column: 7 + }, + expected: ['\'g\''] + }); + }); + + test('prefer longest branch even in a .many()', function() { + var list = Parsimmon.lazy(function() { + return atom.or(sexpr).trim(Parsimmon.optWhitespace).many(); + }); + var atom = Parsimmon.regexp(/[^()\s]+/).desc('an atom'); + var sexpr = list.wrap(Parsimmon.string('('), Parsimmon.string(')')); + + assert.deepEqual( + list.parse('(a b) (c ((() d)))').value, + [['a', 'b'], ['c', [[[], 'd']]]] + ); + + assert.deepEqual(list.parse('(a b ()) c)'), { + status: false, + index: { + offset: 10, + line: 1, + column: 11 + }, + expected: ['\'(\'', 'EOF', 'an atom'] + }); + + assert.deepEqual(list.parse('(a (b)) (() c'), { + status: false, + index: { + offset: 13, + line: 1, + column: 14 + }, + expected: ['\'(\'', '\')\'', 'an atom'] + }); + }); + + test('prefer longest branch in .or() nested in .many()', function() { + var parser = + Parsimmon.string('abc') + .then(Parsimmon.string('def')) + .or(Parsimmon.string('a')) + .many(); + + assert.deepEqual( + parser.parse('aaabcdefaa').value, + ['a', 'a', 'def', 'a', 'a'] + ); + + assert.deepEqual(parser.parse('aaabcde'), { + status: false, + index: { + offset: 5, + line: 1, + column: 6 + }, + expected: ['\'def\''] + }); + }); + +}); diff --git a/test/core/parse.test.js b/test/core/parse.test.js new file mode 100644 index 0000000..a2ea14c --- /dev/null +++ b/test/core/parse.test.js @@ -0,0 +1,20 @@ +'use strict'; + +suite('.parse', function() { + + test('Unique and sorted .expected array', function() { + var parser = + Parsimmon.alt( + Parsimmon.fail('c'), + Parsimmon.fail('a'), + Parsimmon.fail('a'), + Parsimmon.fail('b'), + Parsimmon.fail('b'), + Parsimmon.fail('b'), + Parsimmon.fail('a') + ); + var result = parser.parse(''); + assert.deepEqual(result.expected, ['a', 'b', 'c']); + }); + +}); diff --git a/test/core/range.test.js b/test/core/range.test.js new file mode 100644 index 0000000..eb9e43d --- /dev/null +++ b/test/core/range.test.js @@ -0,0 +1,34 @@ +'use strict'; + +suite('Parsimmon.range', function() { + + var codes = { + a: 'a'.charCodeAt(0), + z: 'z'.charCodeAt(0), + MIN: 0, + MAX: 65535, + }; + var a2z = Parsimmon.range('a', 'z'); + + test('should reject characters before the range', function() { + for (var i = codes.MIN; i < codes.a; i++) { + var s = String.fromCharCode(i); + assert.strictEqual(a2z.parse(s).status, false); + } + }); + + test('should reject characters after the range', function() { + for (var i = codes.z + 1; i <= codes.MAX; i++) { + var s = String.fromCharCode(i); + assert.strictEqual(a2z.parse(s).status, false); + } + }); + + test('should accept characters in the range', function() { + for (var i = codes.a; i <= codes.z; i++) { + var s = String.fromCharCode(i); + assert.strictEqual(a2z.parse(s).status, true); + } + }); + +}); diff --git a/test/core/regexp.test.js b/test/core/regexp.test.js new file mode 100644 index 0000000..f99edc9 --- /dev/null +++ b/test/core/regexp.test.js @@ -0,0 +1,51 @@ +'use strict'; + +suite('Parsimmon.regexp', function() { + + test('general usage', function() { + var parser = Parsimmon.regexp(/[0-9]/); + + assert.equal(parser.parse('1').value, '1'); + assert.equal(parser.parse('4').value, '4'); + assert.deepEqual(parser.parse('x0'), { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['/[0-9]/'] + }); + assert.deepEqual(parser.parse('0x'), { + status: false, + index: { + offset: 1, + line: 1, + column: 2 + }, + expected: ['EOF'] + }); + assert.throws(function() { Parsimmon.regexp(42); }); + assert.throws(function() { Parsimmon.regexp(/a/, 'not a number'); }); + }); + + test('rejects /g flag', function() { + assert.throws(function() { + Parsimmon.regexp(/a/g); + }); + }); + + test('has alias Parsimmon.regex', function() { + assert.equal(Parsimmon.regex, Parsimmon.regexp); + }); + + test('supports groups', function() { + var parser0 = Parsimmon.regexp(/(\w)(\d)/, 0); + var parser1 = Parsimmon.regexp(/(\w)(\d)/, 1); + var parser2 = Parsimmon.regexp(/(\w)(\d)/, 2); + assert.equal(parser0.parse('a1').value, 'a1'); + assert.equal(parser1.parse('a1').value, 'a'); + assert.equal(parser2.parse('a1').value, '1'); + }); + +}); diff --git a/test/core/result.test.js b/test/core/result.test.js new file mode 100644 index 0000000..3e0c0dc --- /dev/null +++ b/test/core/result.test.js @@ -0,0 +1,13 @@ +'use strict'; + +suite('result', function() { + + test('returns a constant result', function() { + var oneParser = Parsimmon.string('x').result(1); + assert.deepEqual( + oneParser.parse('x'), + {status: true, value: 1} + ); + }); + +}); diff --git a/test/core/sepBy.test.js b/test/core/sepBy.test.js new file mode 100644 index 0000000..4b9dba3 --- /dev/null +++ b/test/core/sepBy.test.js @@ -0,0 +1,61 @@ +'use strict'; + +suite('Parsimmon.sepBy/sepBy1', function() { + var chars = Parsimmon.regexp(/[a-zA-Z]+/); + var comma = Parsimmon.string(','); + var csvSep = Parsimmon.sepBy(chars, comma); + var csvSep1 = Parsimmon.sepBy1(chars, comma); + var csvSepB = chars.sepBy(comma); + var csvSep1B = chars.sepBy1(comma); + + test('successful, returns an array of parsed elements', function(){ + var input = 'Heres,a,csv,string,in,our,restrictive,dialect'; + var output = ['Heres', 'a', 'csv', 'string', 'in', 'our', 'restrictive', 'dialect']; + assert.deepEqual(csvSep.parse(input).value, output); + assert.deepEqual(csvSep1.parse(input).value, output); + assert.deepEqual(csvSepB.parse(input).value, output); + assert.deepEqual(csvSep1B.parse(input).value, output); + assert.throws(function() { + Parsimmon.sepBy('not a parser'); + }); + assert.throws(function() { + Parsimmon.sepBy(Parsimmon.string('a'), 'not a parser'); + }); + assert.throws(function() { + Parsimmon.string('a').sepBy('not a parser'); + }); + }); + + test('sepBy succeeds with the empty list on empty input, sepBy1 fails', function() { + assert.deepEqual(csvSep.parse('').value, []); + assert.deepEqual(csvSepB.parse('').value, []); + var failure = { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['/[a-zA-Z]+/'] + }; + assert.deepEqual(csvSep1.parse(''), failure); + assert.deepEqual(csvSep1B.parse(''), failure); + }); + + test('does not tolerate trailing separators', function() { + var input = 'Heres,a,csv,with,a,trailing,comma,'; + var output = { + status: false, + index: { + offset: 34, + line: 1, + column: 35 + }, + expected: ['/[a-zA-Z]+/'] + }; + assert.deepEqual(csvSep.parse(input), output); + assert.deepEqual(csvSep1.parse(input), output); + assert.deepEqual(csvSepB.parse(input), output); + assert.deepEqual(csvSep1B.parse(input), output); + }); +}); diff --git a/test/core/seq.test.js b/test/core/seq.test.js new file mode 100644 index 0000000..e2f228c --- /dev/null +++ b/test/core/seq.test.js @@ -0,0 +1,48 @@ +'use strict'; + +test('Parsimmon.seq', function() { + var parser = + Parsimmon.seq( + Parsimmon.string('('), + Parsimmon.regexp(/[^)]/).many().map(function(xs) { + return xs.join(''); + }), + Parsimmon.string(')') + ); + + assert.deepEqual( + parser.parse('(string between parens)').value, + ['(', 'string between parens', ')'] + ); + + assert.deepEqual( + parser.parse('(string'), + { + status: false, + index: { + offset: 7, + line: 1, + column: 8 + }, + expected: ['\')\'', '/[^)]/'] + } + ); + + assert.deepEqual( + parser.parse('starts wrong (string between parens)'), + { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['\'(\''] + } + ); + + assert.throws(function() { + Parsimmon.seq('not a parser'); + }); +}); + diff --git a/test/core/seqMap.test.js b/test/core/seqMap.test.js new file mode 100644 index 0000000..bd33d5c --- /dev/null +++ b/test/core/seqMap.test.js @@ -0,0 +1,46 @@ +'use strict'; + +suite('Parsimmon.seqMap', function() { + + test('like Parsimmon.seq and .map but spreads arguments', function() { + var add = function(a, b) { return a + b; }; + var parser = Parsimmon.seqMap(Parsimmon.of(1), Parsimmon.of(2), add); + assert.equal(parser.parse('').value, 3); + }); + + test('works for 1 arguments', function() { + var parser = Parsimmon.seqMap(function() { return 10; }); + assert.equal(parser.parse('').value, 10); + }); + + test('works for 100 arguments', function() { + var sum = function() { + var tot = 0; + for (var i = 0; i < arguments.length; i++) { + tot += arguments[i]; + } + return tot; + }; + var args = []; + for (var i = 1; i <= 100; i++) { + args.push(Parsimmon.of(i)); + } + args.push(sum); + var parser = Parsimmon.seqMap.apply(null, args); + assert.equal(parser.parse('').value, 5050); + }); + + test('asserts the final argument is a function', function() { + Parsimmon.seqMap(function() {}); + assert.throws(function() { + Parsimmon.seqMap(1); + }); + }); + + test('asserts at least 1 argument', function() { + assert.throws(function() { + Parsimmon.seqMap(); + }); + }); + +}); diff --git a/test/core/skip.test.js b/test/core/skip.test.js new file mode 100644 index 0000000..778db8f --- /dev/null +++ b/test/core/skip.test.js @@ -0,0 +1,23 @@ +'use strict'; + +suite('skip', function() { + + test('uses the previous return value', function() { + var parser = Parsimmon.string('x').skip(Parsimmon.string('y')); + assert.deepStrictEqual( + parser.parse('xy'), + {status: true, value: 'x'} + ); + assert.strictEqual( + parser.parse('x').status, + false + ); + }); + + test('asserts that a parser was given', function() { + assert.throws(function() { + Parsimmon.string('x').skip('not a parser'); + }); + }); + +}); diff --git a/test/core/string.test.js b/test/core/string.test.js new file mode 100644 index 0000000..09e742f --- /dev/null +++ b/test/core/string.test.js @@ -0,0 +1,26 @@ +'use strict'; + +test('Parsimmon.string', function() { + var parser = Parsimmon.string('x'); + var res = parser.parse('x'); + assert.ok(res.status); + assert.equal(res.value, 'x'); + + res = parser.parse('y'); + assert.deepEqual(res, { + status: false, + index: { + offset: 0, + line: 1, + column: 1 + }, + expected: ['\'x\''] + }); + + assert.equal( + 'expected \'x\' at line 1 column 1, got \'y\'', + Parsimmon.formatError('y', res) + ); + + assert.throws(function() { Parsimmon.string(34); }); +}); diff --git a/test/core/takeWhile.test.js b/test/core/takeWhile.test.js new file mode 100644 index 0000000..468a055 --- /dev/null +++ b/test/core/takeWhile.test.js @@ -0,0 +1,15 @@ +'use strict'; + +test('takeWhile', function() { + var parser = + Parsimmon.takeWhile(function(ch) { + return ch !== '.'; + }).skip(Parsimmon.all); + assert.equal(parser.parse('abc').value, 'abc'); + assert.equal(parser.parse('abc.').value, 'abc'); + assert.equal(parser.parse('.').value, ''); + assert.equal(parser.parse('').value, ''); + assert.throws(function() { + Parsimmon.takeWhile('not a function'); + }); +}); diff --git a/test/core/test.test.js b/test/core/test.test.js new file mode 100644 index 0000000..411f211 --- /dev/null +++ b/test/core/test.test.js @@ -0,0 +1,8 @@ +'use strict'; + +test('test', function() { + var parser = Parsimmon.test(function(ch) { return ch !== '.'; }); + assert.equal(parser.parse('x').value, 'x'); + assert.equal(parser.parse('.').status, false); + assert.throws(function() { Parsimmon.test('not a function'); }); +}); diff --git a/test/core/then.test.js b/test/core/then.test.js new file mode 100644 index 0000000..49ea3d3 --- /dev/null +++ b/test/core/then.test.js @@ -0,0 +1,37 @@ +'use strict'; + +suite('.then', function() { + + test('with a parser, uses the last return value', function() { + var parser = Parsimmon.string('x').then(Parsimmon.string('y')); + assert.deepEqual( + parser.parse('xy'), + {status: true, value: 'y'} + ); + assert.deepEqual(parser.parse('y'), { + status: false, + expected: ['\'x\''], + index: { + offset: 0, + line: 1, + column: 1 + } + }); + assert.deepEqual(parser.parse('xz'), { + status: false, + expected: ['\'y\''], + index: { + offset: 1, + line: 1, + column: 2 + } + }); + }); + + test('errors when argument is not a parser', function() { + assert.throws(function() { + Parsimmon.string('x').then('not a parser'); + }); + }); + +}); diff --git a/test/core/thru.test.js b/test/core/thru.test.js new file mode 100644 index 0000000..8d865cb --- /dev/null +++ b/test/core/thru.test.js @@ -0,0 +1,13 @@ +'use strict'; + +suite('parser.thru', function() { + test('should return wrapper(this)', function() { + function arrayify(x) { + return [x]; + } + var parser = Parsimmon.string(''); + var array = parser.thru(arrayify); + assert.strictEqual(array[0], parser); + }); +}); + diff --git a/test/core/tie.test.js b/test/core/tie.test.js new file mode 100644 index 0000000..83d1111 --- /dev/null +++ b/test/core/tie.test.js @@ -0,0 +1,32 @@ +'use strict'; + +suite('parser.tie()', function() { + + test('concatenates all the results', function() { + var parser = Parsimmon.seq( + Parsimmon.string('<| '), + Parsimmon.letter, + Parsimmon.digit, + Parsimmon.string(' |>') + ).tie(); + var text = '<| o7 |>'; + var result = parser.tryParse(text); + assert.strictEqual(result, text); + }); + + test('only accept array of string parsers', function() { + assert.throws(function() { + Parsimmon.of(1).tie().tryParse(''); + }); + assert.throws(function() { + Parsimmon.of([1]).tie().tryParse(''); + }); + assert.throws(function() { + Parsimmon.of(['1', 2]).tie().tryParse(''); + }); + assert.doesNotThrow(function() { + Parsimmon.of(['1']).tie().tryParse(''); + }); + }); + +}); diff --git a/test/core/times.test.js b/test/core/times.test.js new file mode 100644 index 0000000..1eb93e1 --- /dev/null +++ b/test/core/times.test.js @@ -0,0 +1,75 @@ +'use strict'; + +suite('times', function() { + + test('zero case', function() { + var zeroLetters = Parsimmon.letter.times(0); + assert.deepEqual(zeroLetters.parse('').value, []); + assert.ok(!zeroLetters.parse('x').status); + }); + + test('nonzero case', function() { + var threeLetters = Parsimmon.letter.times(3); + assert.deepEqual(threeLetters.parse('xyz').value, ['x', 'y', 'z']); + assert.ok(!threeLetters.parse('xy').status); + assert.ok(!threeLetters.parse('xyzw').status); + var thenDigit = threeLetters.then(Parsimmon.digit); + assert.equal(thenDigit.parse('xyz1').value, '1'); + assert.ok(!thenDigit.parse('xy1').status); + assert.ok(!thenDigit.parse('xyz').status); + assert.ok(!thenDigit.parse('xyzw').status); + }); + + test('with a min and max', function() { + var someLetters = Parsimmon.letter.times(2, 4); + assert.deepEqual(someLetters.parse('xy').value, ['x', 'y']); + assert.deepEqual(someLetters.parse('xyz').value, ['x', 'y', 'z']); + assert.deepEqual(someLetters.parse('xyzw').value, ['x', 'y', 'z', 'w']); + assert.ok(!someLetters.parse('xyzwv').status); + assert.ok(!someLetters.parse('x').status); + var thenDigit = someLetters.then(Parsimmon.digit); + assert.equal(thenDigit.parse('xy1').value, '1'); + assert.equal(thenDigit.parse('xyz1').value, '1'); + assert.equal(thenDigit.parse('xyzw1').value, '1'); + assert.ok(!thenDigit.parse('xy').status); + assert.ok(!thenDigit.parse('xyzw').status); + assert.ok(!thenDigit.parse('xyzwv1').status); + assert.ok(!thenDigit.parse('x1').status); + }); + + test('checks that argument types are correct', function() { + assert.throws(function() { Parsimmon.string('x').times('not a number'); }); + assert.throws(function() { Parsimmon.string('x').times(1, 'not a number'); }); + assert.throws(function() { Parsimmon.string('x').atLeast('not a number'); }); + assert.throws(function() { Parsimmon.string('x').atMost('not a number'); }); + }); + + test('prefer longest branch in .times() too', function() { + var parser = + Parsimmon.string('abc') + .then(Parsimmon.string('def')) + .or(Parsimmon.string('a')) + .times(3, 6); + + assert.deepEqual(parser.parse('aabcde'), { + status: false, + index: { + offset: 4, + line: 1, + column: 5 + }, + expected: ['\'def\''] + }); + + assert.deepEqual(parser.parse('aaaaabcde'), { + status: false, + index: { + offset: 7, + line: 1, + column: 8 + }, + expected: ['\'def\''] + }); + }); + +}); diff --git a/test/core/trim.test.js b/test/core/trim.test.js new file mode 100644 index 0000000..8ea75ac --- /dev/null +++ b/test/core/trim.test.js @@ -0,0 +1,11 @@ +'use strict'; + +suite('parser.trim', function() { + + test('should remove stuff from the begin and end', function() { + var parser = Parsimmon.letters.trim(Parsimmon.whitespace); + var value = parser.tryParse('\t\n NICE \t\t '); + assert.strictEqual(value, 'NICE'); + }); + +}); diff --git a/test/core/tryParse.test.js b/test/core/tryParse.test.js new file mode 100644 index 0000000..33efc2e --- /dev/null +++ b/test/core/tryParse.test.js @@ -0,0 +1,47 @@ +'use strict'; + +suite('.tryParse', function() { + test('returns just the value', function() { + var x = 4; + assert.equal(Parsimmon.of(x).tryParse(''), x); + }); + + test('returns throws on a bad parse', function() { + assert.throws(function() { + Parsimmon.digit.tryParse('a'); + }); + }); + + test('thrown error message is equal to formatError', function() { + var input = 'a'; + var parser = Parsimmon.digit; + var result = parser.parse(input); + var errMsg = Parsimmon.formatError(input, result); + try { + parser.tryParse(input); + } catch (err) { + assert.equal(err.message, errMsg); + } + }); + + test('thrown error contains full result object', function() { + var input = 'a'; + var parser = Parsimmon.digit; + var result = parser.parse(input); + try { + parser.tryParse(input); + } catch (err) { + assert.deepEqual(err.result, result); + } + }); + + test('thrown error message is equal to formatError', function() { + var input = 'a'; + var parser = Parsimmon.digit; + try { + parser.tryParse(input); + } catch (err) { + assert.deepEqual(err.result, parser.parse(input)); + } + }); +}); diff --git a/test/core/wrap.test.js b/test/core/wrap.test.js new file mode 100644 index 0000000..ef0a933 --- /dev/null +++ b/test/core/wrap.test.js @@ -0,0 +1,13 @@ +'use strict'; + +suite('parser.wrap', function() { + + test('should remove different stuff from the begin and end', function() { + var lParen = Parsimmon.string('('); + var rParen = Parsimmon.string(')'); + var parser = Parsimmon.letters.wrap(lParen, rParen); + var value = parser.tryParse('(heyyy)'); + assert.strictEqual(value, 'heyyy'); + }); + +}); diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..0bb80a4 --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,9 @@ +'use strict'; + +function equivalentParsers(p1, p2, inputs) { + for (var i = 0; i < inputs.length; i++) { + assert.deepEqual(p1.parse(inputs[i]), p2.parse(inputs[i])); + } +} + +exports.equivalentParsers = equivalentParsers; diff --git a/test/intro.js b/test/intro.js deleted file mode 100644 index 4e37ebd..0000000 --- a/test/intro.js +++ /dev/null @@ -1,3 +0,0 @@ -global.assert = require('chai').assert; -global.Parsimmon = require('../src/parsimmon'); -global.mocha = require('mocha'); diff --git a/test/laws/applicative.test.js b/test/laws/applicative.test.js new file mode 100644 index 0000000..866e3b4 --- /dev/null +++ b/test/laws/applicative.test.js @@ -0,0 +1,39 @@ +'use strict'; + +var H = require('../helpers'); + +suite('Fantasy Land Applicative', function() { + test('identity', function() { + var p1 = Parsimmon.any; + var p2 = p1.ap(Parsimmon.of(function(x) { return x; })); + H.equivalentParsers(p1, p2, [ + 'x', + 'z', + 'æ', + '1', + '' + ]); + }); + + test('homomorphism', function() { + function fn(s) { + return s.toUpperCase(); + } + var input = 'nice'; + var p1 = Parsimmon.of(input).ap(Parsimmon.of(fn)); + var p2 = Parsimmon.of(fn(input)); + assert.deepEqual(p1.parse(''), p2.parse('')); + }); + + test('interchange', function() { + function increment(x) { + return x + 1; + } + var input = 3; + var p1 = Parsimmon.of(input).ap(Parsimmon.of(increment)); + var p2 = Parsimmon.of(increment).ap(Parsimmon.of(function(f) { + return f(input); + })); + assert.deepEqual(p1.parse(''), p2.parse('')); + }); +}); diff --git a/test/laws/apply.test.js b/test/laws/apply.test.js new file mode 100644 index 0000000..c07adac --- /dev/null +++ b/test/laws/apply.test.js @@ -0,0 +1,39 @@ +'use strict'; + +var H = require('../helpers'); + +suite('Fantasy Land Apply', function() { + test('composition', function() { + function reverse(s) { + return s.split('').reverse().join(''); + } + + function upperCase(s) { + return s.toUpperCase(); + } + + function compose(f) { + return function(g) { + return function(x) { + return f(g(x)); + }; + }; + } + + var p1 = + Parsimmon.all + .ap(Parsimmon.of(reverse)) + .ap(Parsimmon.of(upperCase)); + + var p2 = + Parsimmon.all.ap( + Parsimmon.of(reverse).ap( + Parsimmon.of(upperCase).map(compose) + ) + ); + + H.equivalentParsers(p1, p2, [ + 'ok cool' + ]); + }); +}); diff --git a/test/laws/chain.test.js b/test/laws/chain.test.js new file mode 100644 index 0000000..b3db6e9 --- /dev/null +++ b/test/laws/chain.test.js @@ -0,0 +1,28 @@ +'use strict'; + +suite('Fantasy Land Chain', function() { + test('associativity', function() { + function appender(x) { + return function(xs) { + return Parsimmon.of(xs.concat(x)); + }; + } + function reverse(xs) { + return Parsimmon.of(xs.slice().reverse()); + } + var list = Parsimmon.sepBy(Parsimmon.letters, Parsimmon.whitespace); + var input = 'quuz foo bar baz'; + var output = { + status: true, + value: ['baz', 'bar', 'foo', 'quuz', 'aaa'] + }; + var p1 = list.chain(reverse).chain(appender('aaa')); + var p2 = list.chain(function(x) { + return reverse(x).chain(appender('aaa')); + }); + var out1 = p1.parse(input); + var out2 = p2.parse(input); + assert.deepEqual(out1, out2); + assert.deepEqual(out1, output); + }); +}); diff --git a/test/laws/fantasy-land-aliases.test.js b/test/laws/fantasy-land-aliases.test.js new file mode 100644 index 0000000..d2e89fd --- /dev/null +++ b/test/laws/fantasy-land-aliases.test.js @@ -0,0 +1,32 @@ +'use strict'; + +suite('fantasy-land/* method aliases', function() { + function makeTester(name) { + return function() { + var flName = 'fantasy-land/' + name; + var parser = Parsimmon.of('burrito'); + assert.equal(parser[name], parser[flName]); + }; + } + var methods = [ + 'ap', + 'chain', + 'concat', + 'empty', + 'map', + 'of' + ]; + for (var i = 0; i < methods.length; i++) { + test('fantasy-land/' + methods[i] + ' alias', makeTester(methods[i])); + } + + test('Fantasy Land Parsimmon.empty alias', function() { + assert.equal(Parsimmon.empty, Parsimmon['fantasy-land/empty']); + }); + + test('Fantasy Land Parsimmon.of alias', function() { + assert.equal(Parsimmon.of, Parsimmon['fantasy-land/of']); + assert.equal(Parsimmon.of, Parsimmon.any.of); + }); + +}); diff --git a/test/laws/functor.test.js b/test/laws/functor.test.js new file mode 100644 index 0000000..0c42359 --- /dev/null +++ b/test/laws/functor.test.js @@ -0,0 +1,32 @@ +'use strict'; + +var H = require('../helpers'); + +suite('Fantasy Land Functor', function() { + test('identity', function() { + var p1 = Parsimmon.digits; + var p2 = Parsimmon.digits.map(function(x) { return x; }); + H.equivalentParsers(p1, p2, [ + '091', + '111111', + '46782792', + 'oops' + ]); + }); + + test('composition', function() { + function increment(x) { + return x + 1; + } + var p1 = Parsimmon.digits.map(function(x) { + return increment(Number(x)); + }); + var p2 = Parsimmon.digits.map(Number).map(increment); + H.equivalentParsers(p1, p2, [ + '12', + '98789', + '89772371298389217387128937979839821738', + 'oh no!' + ]); + }); +}); diff --git a/test/laws/monad.test.js b/test/laws/monad.test.js new file mode 100644 index 0000000..36050f9 --- /dev/null +++ b/test/laws/monad.test.js @@ -0,0 +1,36 @@ +'use strict'; + +suite('Fantasy Land Monad', function() { + + test('left identity', function() { + function upperCase(x) { + return Parsimmon.of(x.toUpperCase()); + } + var input = 'foo'; + var output = { + status: true, + value: 'FOO' + }; + var p1 = Parsimmon.of(input).chain(upperCase); + var p2 = upperCase(input); + var out1 = p1.parse(''); + var out2 = p2.parse(''); + assert.deepEqual(out1, out2); + assert.deepEqual(out1, output); + }); + + test('right identity', function() { + var input = 'monad burrito'; + var output = { + status: true, + value: input + }; + var p1 = Parsimmon.all.chain(Parsimmon.of); + var p2 = Parsimmon.all; + var out1 = p1.parse(input); + var out2 = p2.parse(input); + assert.deepEqual(out1, out2); + assert.deepEqual(out1, output); + }); + +}); diff --git a/test/laws/semigroup.test.js b/test/laws/semigroup.test.js new file mode 100644 index 0000000..a52f0fb --- /dev/null +++ b/test/laws/semigroup.test.js @@ -0,0 +1,17 @@ +'use strict'; + +var H = require('../helpers'); + +suite('Fantasy Land Semigroup', function() { + test('associativity', function() { + var a = Parsimmon.string('a'); + var b = Parsimmon.string('b'); + var c = Parsimmon.string('c'); + var abc1 = a.concat(b).concat(c); + var abc2 = a.concat(b.concat(c)); + H.equivalentParsers(abc1, abc2, [ + 'abc', + 'ac' + ]); + }); +}); diff --git a/test/parsimmon.test.js b/test/parsimmon.test.js deleted file mode 100644 index 8adfb01..0000000 --- a/test/parsimmon.test.js +++ /dev/null @@ -1,1456 +0,0 @@ -/* global suite, test, assert, Parsimmon */ - -suite('parser', function() { - var string = Parsimmon.string; - var regex = Parsimmon.regex; - var letter = Parsimmon.letter; - var digit = Parsimmon.digit; - var any = Parsimmon.any; - var optWhitespace = Parsimmon.optWhitespace; - var eof = Parsimmon.eof; - var seq = Parsimmon.seq; - var alt = Parsimmon.alt; - var all = Parsimmon.all; - var index = Parsimmon.index; - var lazy = Parsimmon.lazy; - var fail = Parsimmon.fail; - var lookahead = Parsimmon.lookahead; - var notFollowedBy = Parsimmon.notFollowedBy; - - function equivalentParsers(p1, p2, inputs) { - for (var i = 0; i < inputs.length; i++) { - assert.deepEqual(p1.parse(inputs[i]), p2.parse(inputs[i])); - } - } - - test('Parsimmon.isParser', function() { - assert.isFalse(Parsimmon.isParser(undefined)); - assert.isFalse(Parsimmon.isParser({})); - assert.isFalse(Parsimmon.isParser(null)); - assert.isTrue(Parsimmon.isParser(string('x'))); - assert.isTrue(Parsimmon.isParser(regex(/[0-9]/))); - }); - - test('Parsimmon.makeSuccess', function() { - var index = 11; - var value = 'a lucky number'; - var result = Parsimmon.makeSuccess(index, value); - assert.deepEqual(result, { - status: true, - index: index, - value: value, - furthest: -1, - expected: [] - }); - }); - - test('Parsimmon.makeFailure', function() { - var furthest = 4444; - var expected = 'waiting in the clock tower'; - var result = Parsimmon.makeFailure(furthest, expected); - assert.deepEqual(result, { - status: false, - index: -1, - value: null, - furthest: furthest, - expected: [expected] - }); - }); - - test('Parsimmon()', function() { - var good = 'just a Q'; - var bad = 'all I wanted was a Q'; - var justQ = Parsimmon(function(str, i) { - if (str.charAt(i) === 'Q') { - return Parsimmon.makeSuccess(i + 1, good); - } else { - return Parsimmon.makeFailure(i, bad); - } - }); - var result1 = justQ.parse('Q'); - var result2 = justQ.parse('x'); - assert.deepEqual(result1, { - status: true, - value: good, - }); - assert.deepEqual(result2, { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: [bad] - }); - }); - - test('Parsimmon.string', function() { - var parser = string('x'); - var res = parser.parse('x'); - assert.ok(res.status); - assert.equal(res.value, 'x'); - - res = parser.parse('y'); - assert.deepEqual(res, { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['\'x\''] - }); - - assert.equal( - 'expected \'x\' at line 1 column 1, got \'y\'', - Parsimmon.formatError('y', res) - ); - - assert.throws(function() { string(34); }); - }); - - test('Parsimmon.formatError', function() { - var parser = - Parsimmon.alt( - Parsimmon.fail('a'), - Parsimmon.fail('b'), - Parsimmon.fail('c') - ); - var expectation = 'expected one of a, b, c, got the end of the input'; - var input = ''; - var answer = Parsimmon.formatError(input, parser.parse(input)); - assert.deepEqual(answer, expectation); - }); - - suite('Parsimmon.notFollowedBy', function() { - test('fails when its parser argument matches', function() { - var weirdParser = string('dx'); - var parser = seq( - string('abc'), - notFollowedBy(weirdParser).result('NOT USED'), - string('dx') - ); - var answer = parser.parse('abcdx'); - assert.deepEqual(answer.expected, ['not "dx"']); - }); - test('does not consume from its input', function() { - var weirdParser = string('Q'); - var parser = seq( - string('abc'), - notFollowedBy(weirdParser), - string('d') - ); - var answer = parser.parse('abcd'); - assert.deepEqual(answer.value, ['abc', null, 'd']); - }); - test('can be chained from prototype', function() { - var parser = seq( - string('abc').notFollowedBy(string('Q')), - string('d') - ); - var answer = parser.parse('abcd'); - assert.deepEqual(answer.value, ['abc', 'd']); - }); - }); - - suite('Parsimmon.createLanguage', function() { - test('should return an object of parsers', function() { - var lang = Parsimmon.createLanguage({ - a: function() { - return Parsimmon.string('a'); - }, - b: function() { - return Parsimmon.string('b'); - } - }); - assert.ok(Parsimmon.isParser(lang.a)); - assert.ok(Parsimmon.isParser(lang.b)); - }); - test('should allow direct recursion in parsers', function() { - var lang = Parsimmon.createLanguage({ - Parentheses: function(r) { - return Parsimmon.alt( - Parsimmon.string('()'), - Parsimmon.string('(') - .then(r.Parentheses) - .skip(Parsimmon.string(')')) - ); - } - }); - lang.Parentheses.tryParse('(((())))'); - }); - test('should allow indirect recursion in parsers', function() { - var lang = Parsimmon.createLanguage({ - Value: function(r) { - return Parsimmon.alt( - r.Number, - r.Symbol, - r.List - ); - }, - Number: function() { - return Parsimmon.regexp(/[0-9]+/).map(Number); - }, - Symbol: function() { - return Parsimmon.regexp(/[a-z]+/); - }, - List: function(r) { - return Parsimmon.string('(') - .then(Parsimmon.sepBy(r.Value, r._)) - .skip(Parsimmon.string(')')); - }, - _: function() { - return Parsimmon.optWhitespace; - } - }); - lang.Value.tryParse('(list 1 2 foo (list nice 3 56 989 asdasdas))'); - }); - }); - - suite('parser.thru', function() { - test('should return wrapper(this)', function() { - function arrayify(x) { - return [x]; - } - var parser = Parsimmon.string(''); - var array = parser.thru(arrayify); - assert.strictEqual(array[0], parser); - }); - }); - - suite('Parsimmon.lookahead', function() { - test('should handle a string', function() { - lookahead(''); - }); - test('should handle a regexp', function() { - lookahead(/./); - }); - test('should handle a parser', function() { - lookahead(Parsimmon.digit); - }); - test('can be chained as prototype', function() { - var parser = seq( - string('abc').lookahead('d'), - string('d') - ); - var answer = parser.parse('abcd'); - assert.deepEqual(answer.value, ['abc', 'd']); - }); - test('does not consume from a string', function() { - var parser = seq( - string('abc'), - lookahead('d'), - string('d') - ); - var answer = parser.parse('abcd'); - assert.deepEqual(answer.value, ['abc', '', 'd']); - }); - test('does not consume from a regexp', function() { - var parser = seq( - string('abc'), - lookahead(/d/), - string('d') - ); - var answer = parser.parse('abcd'); - assert.deepEqual(answer.value, ['abc', '', 'd']); - }); - test('does not consume from a parser', function() { - var weirdParser = Parsimmon.string('Q').or(Parsimmon.string('d')); - var parser = seq( - string('abc'), - lookahead(weirdParser), - string('d') - ); - var answer = parser.parse('abcd'); - assert.deepEqual(answer.value, ['abc', '', 'd']); - }); - test('raises error if argument is not a string, regexp, or parser', function() { - assert.throws(function() { lookahead({}); }); - assert.throws(function() { lookahead([]); }); - assert.throws(function() { lookahead(true); }); - assert.throws(function() { lookahead(12); }); - }); - }); - - test('Parsimmon.regex', function() { - var parser = regex(/[0-9]/); - - assert.equal(parser.parse('1').value, '1'); - assert.equal(parser.parse('4').value, '4'); - assert.deepEqual(parser.parse('x0'), { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['/[0-9]/'] - }); - assert.deepEqual(parser.parse('0x'), { - status: false, - index: { - offset: 1, - line: 1, - column: 2 - }, - expected: ['EOF'] - }); - assert.throws(function() { regex(42); }); - assert.throws(function() { regex(/a/, 'not a number'); }); - }); - - test('Parsimmon.regex rejects /g flag', function() { - assert.throws(function() { - regex(/a/g); - }); - }); - - test('Parsimmon.regex is an alias for Parsimmon.regexp', function() { - assert.equal(Parsimmon.regex, Parsimmon.regexp); - }); - - test('Parsimmon.regex with group', function() { - var parser0 = regex(/(\w)(\d)/, 0); - var parser1 = regex(/(\w)(\d)/, 1); - var parser2 = regex(/(\w)(\d)/, 2); - assert.equal(parser0.parse('a1').value, 'a1'); - assert.equal(parser1.parse('a1').value, 'a'); - assert.equal(parser2.parse('a1').value, '1'); - }); - - test('Parsimmon.seq', function() { - var parser = - seq( - string('('), - regex(/[^)]/).many().map(function(xs) { - return xs.join(''); - }), - string(')') - ); - - assert.deepEqual( - parser.parse('(string between parens)').value, - ['(', 'string between parens', ')'] - ); - - assert.deepEqual( - parser.parse('(string'), - { - status: false, - index: { - offset: 7, - line: 1, - column: 8 - }, - expected: ['\')\'', '/[^)]/'] - } - ); - - assert.deepEqual( - parser.parse('starts wrong (string between parens)'), - { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['\'(\''] - } - ); - - assert.throws(function() { - seq('not a parser'); - }); - }); - - suite('Parsimmon.seqMap', function() { - test('like Parsimmon.seq and .map but spreads arguments', function() { - var add = function(a, b) { return a + b; }; - var parser = Parsimmon.seqMap(Parsimmon.of(1), Parsimmon.of(2), add); - assert.equal(parser.parse('').value, 3); - }); - - test('works for 1 arguments', function() { - var parser = Parsimmon.seqMap(function() { return 10; }); - assert.equal(parser.parse('').value, 10); - }); - - test('works for 100 arguments', function() { - var sum = function() { - var tot = 0; - for (var i = 0; i < arguments.length; i++) { - tot += arguments[i]; - } - return tot; - }; - var args = []; - for (var i = 1; i <= 100; i++) { - args.push(Parsimmon.of(i)); - } - args.push(sum); - var parser = Parsimmon.seqMap.apply(null, args); - assert.equal(parser.parse('').value, 5050); - }); - - test('asserts the final argument is a function', function() { - Parsimmon.seqMap(function() {}); - assert.throws(function() { - Parsimmon.seqMap(1); - }); - }); - - test('asserts at least 1 argument', function() { - assert.throws(function() { - Parsimmon.seqMap(); - }); - }); - }); - - suite('Parsimmon.custom', function(){ - test('simple parser definition', function(){ - function customAny() { - return Parsimmon.custom(function(success){ - return function(input, i) { - return success(i+1, input.charAt(i)); - }; - }); - } - - var letters = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']; - var parser = customAny(); - - for (var i = 0; i < letters.length; i++) { - assert.deepEqual(parser.parse(letters[i]), {status: true, value: letters[i]}); - } - }); - - test('failing parser', function(){ - function failer() { - return Parsimmon.custom(function(success, failure){ - return function(input, i) { - return failure(i, 'nothing'); - }; - }); - } - - assert.deepEqual(failer().parse('a'), { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['nothing'] - }); - }); - - test('composes with existing parsers', function(){ - function notChar(char) { - return Parsimmon.custom(function(success, failure) { - return function(input, i) { - if (input.charCodeAt(i) !== char.charCodeAt(0)) { - return success(i+1, input.charAt(i)); - } - return failure(i, 'something different than "' + input.charAt(i)) + '"'; - }; - }); - } - - function join(array) { - return array.join(''); - } - - var parser = seq(string('a'), notChar('b').times(5).map(join), notChar('b').or(string('b'))).map(join); - - assert.deepEqual(parser.parse('acccccb'), {status: true, value: 'acccccb'}); - }); - }); - - suite('.tryParse', function() { - test('returns just the value', function() { - var x = 4; - assert.equal(Parsimmon.of(x).tryParse(''), x); - }); - - test('returns throws on a bad parse', function() { - assert.throws(function() { - Parsimmon.digit.tryParse('a'); - }); - }); - - test('thrown error message is equal to formatError', function() { - var input = 'a'; - var parser = Parsimmon.digit; - var result = parser.parse(input); - var errMsg = Parsimmon.formatError(input, result); - try { - parser.tryParse(input); - } catch (err) { - assert.equal(err.message, errMsg); - } - }); - - test('thrown error contains full result object', function() { - var input = 'a'; - var parser = Parsimmon.digit; - var result = parser.parse(input); - try { - parser.tryParse(input); - } catch (err) { - assert.deepEqual(err.result, result); - } - }); - - test('thrown error message is equal to formatError', function() { - var input = 'a'; - var parser = Parsimmon.digit; - try { - parser.tryParse(input); - } catch (err) { - assert.deepEqual(err.result, parser.parse(input)); - } - }); - }); - - test('Unique and sorted .expected array', function() { - var parser = - alt( - fail('c'), - fail('a'), - fail('a'), - fail('b'), - fail('b'), - fail('b'), - fail('a') - ); - var result = parser.parse(''); - assert.deepEqual(result.expected, ['a', 'b', 'c']); - }); - - test('Parsimmon.alt', function(){ - var toNode = function(nodeType){ - return function(value) { - return {type: nodeType, value: value}; - }; - }; - - var stringParser = seq(string('"'), regex(/[^"]*/), string('"')).map(toNode('string')); - var identifierParser = regex(/[a-zA-Z]*/).map(toNode('identifier')); - var parser = alt(stringParser, identifierParser); - - assert.deepEqual(parser.parse('"a string, to be sure"').value, { - type: 'string', - value: ['"', 'a string, to be sure', '"'] - }); - assert.deepEqual(parser.parse('anIdentifier').value, { - type: 'identifier', - value: 'anIdentifier' - }); - - assert.throws(function() { alt('not a parser'); }); - }); - - test('.empty()', function() { - var emptyParse = { - status: false, - expected: ['fantasy-land/empty'], - index: {offset: 0, line: 1, column: 1} - }; - assert.deepEqual(Parsimmon.digit.empty, Parsimmon.empty); - assert.deepEqual(Parsimmon.empty().parse(''), emptyParse); - }); - - suite('fantasy-land/* method aliases', function() { - function makeTester(name) { - return function() { - var flName = 'fantasy-land/' + name; - var parser = Parsimmon.of('burrito'); - assert.equal(parser[name], parser[flName]); - }; - } - var methods = [ - 'ap', - 'chain', - 'concat', - 'empty', - 'map', - 'of' - ]; - for (var i = 0; i < methods.length; i++) { - test('fantasy-land/' + methods[i] + ' alias', makeTester(methods[i])); - } - }); - - test('Fantasy Land Parsimmon.empty alias', function() { - assert.equal(Parsimmon.empty, Parsimmon['fantasy-land/empty']); - }); - - test('Fantasy Land Parsimmon.of alias', function() { - assert.equal(Parsimmon.of, Parsimmon['fantasy-land/of']); - assert.equal(Parsimmon.of, Parsimmon.any.of); - }); - - suite('Fantasy Land Semigroup', function() { - test('associativity', function() { - var a = Parsimmon.string('a'); - var b = Parsimmon.string('b'); - var c = Parsimmon.string('c'); - var abc1 = a.concat(b).concat(c); - var abc2 = a.concat(b.concat(c)); - equivalentParsers(abc1, abc2, [ - 'abc', - 'ac' - ]); - }); - }); - - suite('Fantasy Land Functor', function() { - test('identity', function() { - var p1 = Parsimmon.digits; - var p2 = Parsimmon.digits.map(function(x) { return x; }); - equivalentParsers(p1, p2, [ - '091', - '111111', - '46782792', - 'oops' - ]); - }); - - test('composition', function() { - function increment(x) { - return x + 1; - } - var p1 = Parsimmon.digits.map(function(x) { - return increment(Number(x)); - }); - var p2 = Parsimmon.digits.map(Number).map(increment); - equivalentParsers(p1, p2, [ - '12', - '98789', - '89772371298389217387128937979839821738', - 'oh no!' - ]); - }); - }); - - suite('Fantasy Land Apply', function() { - test('composition', function() { - function reverse(s) { - return s.split('').reverse().join(''); - } - - function upperCase(s) { - return s.toUpperCase(); - } - - function compose(f) { - return function(g) { - return function(x) { - return f(g(x)); - }; - }; - } - - var p1 = - Parsimmon.all - .ap(Parsimmon.of(reverse)) - .ap(Parsimmon.of(upperCase)); - - var p2 = - Parsimmon.all.ap( - Parsimmon.of(reverse).ap( - Parsimmon.of(upperCase).map(compose) - ) - ); - - equivalentParsers(p1, p2, [ - 'ok cool' - ]); - }); - }); - - suite('Fantasy Land Applicative', function() { - test('identity', function() { - var p1 = Parsimmon.any; - var p2 = p1.ap(Parsimmon.of(function(x) { return x; })); - equivalentParsers(p1, p2, [ - 'x', - 'z', - 'æ', - '1', - '' - ]); - }); - - test('homomorphism', function() { - function fn(s) { - return s.toUpperCase(); - } - var input = 'nice'; - var p1 = Parsimmon.of(input).ap(Parsimmon.of(fn)); - var p2 = Parsimmon.of(fn(input)); - assert.deepEqual(p1.parse(''), p2.parse('')); - }); - - test('interchange', function() { - function increment(x) { - return x + 1; - } - var input = 3; - var p1 = Parsimmon.of(input).ap(Parsimmon.of(increment)); - var p2 = Parsimmon.of(increment).ap(Parsimmon.of(function(f) { - return f(input); - })); - assert.deepEqual(p1.parse(''), p2.parse('')); - }); - }); - - suite('Fantasy Land Chain', function() { - test('associativity', function() { - function appender(x) { - return function(xs) { - return Parsimmon.of(xs.concat(x)); - }; - } - function reverse(xs) { - return Parsimmon.of(xs.slice().reverse()); - } - var list = Parsimmon.sepBy(Parsimmon.letters, Parsimmon.whitespace); - var input = 'quuz foo bar baz'; - var output = { - status: true, - value: ['baz', 'bar', 'foo', 'quuz', 'aaa'] - }; - var p1 = list.chain(reverse).chain(appender('aaa')); - var p2 = list.chain(function(x) { - return reverse(x).chain(appender('aaa')); - }); - var out1 = p1.parse(input); - var out2 = p2.parse(input); - assert.deepEqual(out1, out2); - assert.deepEqual(out1, output); - }); - }); - - suite('Fantasy Land Monad', function() { - test('left identity', function() { - function upperCase(x) { - return Parsimmon.of(x.toUpperCase()); - } - var input = 'foo'; - var output = { - status: true, - value: 'FOO' - }; - var p1 = Parsimmon.of(input).chain(upperCase); - var p2 = upperCase(input); - var out1 = p1.parse(''); - var out2 = p2.parse(''); - assert.deepEqual(out1, out2); - assert.deepEqual(out1, output); - }); - - test('right identity', function() { - var input = 'monad burrito'; - var output = { - status: true, - value: input - }; - var p1 = Parsimmon.all.chain(Parsimmon.of); - var p2 = Parsimmon.all; - var out1 = p1.parse(input); - var out2 = p2.parse(input); - assert.deepEqual(out1, out2); - assert.deepEqual(out1, output); - }); - }); - - suite('Parsimmon.sepBy/sepBy1', function() { - var chars = regex(/[a-zA-Z]+/); - var comma = string(','); - var csvSep = Parsimmon.sepBy(chars, comma); - var csvSep1 = Parsimmon.sepBy1(chars, comma); - var csvSepB = chars.sepBy(comma); - var csvSep1B = chars.sepBy1(comma); - - test('successful, returns an array of parsed elements', function(){ - var input = 'Heres,a,csv,string,in,our,restrictive,dialect'; - var output = ['Heres', 'a', 'csv', 'string', 'in', 'our', 'restrictive', 'dialect']; - assert.deepEqual(csvSep.parse(input).value, output); - assert.deepEqual(csvSep1.parse(input).value, output); - assert.deepEqual(csvSepB.parse(input).value, output); - assert.deepEqual(csvSep1B.parse(input).value, output); - assert.throws(function() { - Parsimmon.sepBy('not a parser'); - }); - assert.throws(function() { - Parsimmon.sepBy(string('a'), 'not a parser'); - }); - assert.throws(function() { - string('a').sepBy('not a parser'); - }); - }); - - test('sepBy succeeds with the empty list on empty input, sepBy1 fails', function() { - assert.deepEqual(csvSep.parse('').value, []); - assert.deepEqual(csvSepB.parse('').value, []); - var failure = { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['/[a-zA-Z]+/'] - }; - assert.deepEqual(csvSep1.parse(''), failure); - assert.deepEqual(csvSep1B.parse(''), failure); - }); - - test('does not tolerate trailing separators', function() { - var input = 'Heres,a,csv,with,a,trailing,comma,'; - var output = { - status: false, - index: { - offset: 34, - line: 1, - column: 35 - }, - expected: ['/[a-zA-Z]+/'] - }; - assert.deepEqual(csvSep.parse(input), output); - assert.deepEqual(csvSep1.parse(input), output); - assert.deepEqual(csvSepB.parse(input), output); - assert.deepEqual(csvSep1B.parse(input), output); - }); - }); - - suite('parser.tie()', function() { - test('concatenates all the results', function() { - var parser = Parsimmon.seq( - Parsimmon.string('<| '), - Parsimmon.letter, - Parsimmon.digit, - Parsimmon.string(' |>') - ).tie(); - var text = '<| o7 |>'; - var result = parser.tryParse(text); - assert.strictEqual(result, text); - }); - - test('only accept array of string parsers', function() { - assert.throws(function() { - Parsimmon.of(1).tie().tryParse(''); - }); - assert.throws(function() { - Parsimmon.of([1]).tie().tryParse(''); - }); - assert.throws(function() { - Parsimmon.of(['1', 2]).tie().tryParse(''); - }); - assert.doesNotThrow(function() { - Parsimmon.of(['1']).tie().tryParse(''); - }); - }); - }); - - suite('Parsimmon.range', function() { - var codes = { - a: 'a'.charCodeAt(0), - z: 'z'.charCodeAt(0), - MIN: 0, - MAX: 65535, - }; - var a2z = Parsimmon.range('a', 'z'); - - test('should reject characters before the range', function() { - for (var i = codes.MIN; i < codes.a; i++) { - var s = String.fromCharCode(i); - assert.strictEqual(a2z.parse(s).status, false); - } - }); - - test('should reject characters after the range', function() { - for (var i = codes.z + 1; i <= codes.MAX; i++) { - var s = String.fromCharCode(i); - assert.strictEqual(a2z.parse(s).status, false); - } - }); - - test('should accept characters in the range', function() { - for (var i = codes.a; i <= codes.z; i++) { - var s = String.fromCharCode(i); - assert.strictEqual(a2z.parse(s).status, true); - } - }); - }); - - suite('parser.trim', function() { - test('should remove stuff from the begin and end', function() { - var parser = Parsimmon.letters.trim(Parsimmon.whitespace); - var value = parser.tryParse('\t\n NICE \t\t '); - assert.strictEqual(value, 'NICE'); - }); - }); - - suite('parser.wrap', function() { - test('should remove different stuff from the begin and end', function() { - var lParen = Parsimmon.string('('); - var rParen = Parsimmon.string(')'); - var parser = Parsimmon.letters.wrap(lParen, rParen); - var value = parser.tryParse('(heyyy)'); - assert.strictEqual(value, 'heyyy'); - }); - }); - - suite('then', function() { - test('with a parser, uses the last return value', function() { - var parser = string('x').then(string('y')); - assert.deepEqual(parser.parse('xy'), {status: true, value: 'y'}); - assert.deepEqual(parser.parse('y'), { - status: false, - expected: ['\'x\''], - index: { - offset: 0, - line: 1, - column: 1 - } - }); - assert.deepEqual(parser.parse('xz'), { - status: false, - expected: ['\'y\''], - index: { - offset: 1, - line: 1, - column: 2 - } - }); - }); - test('errors when argument is not a parser', function() { - assert.throws(function() { - string('x').then('not a parser'); - }); - }); - }); - - suite('chain', function() { - test('asserts that a parser is returned', function() { - var parser1 = letter.chain(function() { return 'not a parser'; }); - assert.throws(function() { parser1.parse('x'); }); - - assert.throws(function() { letter.then('x'); }); - }); - - test('with a function that returns a parser, continues with that parser', function() { - var piped; - var parser = string('x').chain(function(x) { - piped = x; - return string('y'); - }); - - assert.deepEqual(parser.parse('xy'), {status: true, value: 'y'}); - assert.equal(piped, 'x'); - assert.ok(!parser.parse('x').status); - }); - }); - - suite('map', function() { - test('with a function, pipes the value in and uses that return value', function() { - var piped; - - var parser = string('x').map(function(x) { - piped = x; - return 'y'; - }); - - assert.deepEqual(parser.parse('x'), {status: true, value: 'y'}); - assert.equal(piped, 'x'); - }); - test('asserts that a function was given', function() { - assert.throws(function() { string('x').map('not a function'); }); - }); - }); - - suite('result', function() { - test('returns a constant result', function() { - var oneParser = string('x').result(1); - assert.deepEqual(oneParser.parse('x'), {status: true, value: 1}); - }); - }); - - suite('skip', function() { - test('uses the previous return value', function() { - var parser = string('x').skip(string('y')); - - assert.deepEqual(parser.parse('xy'), {status: true, value: 'x'}); - assert.ok(!parser.parse('x').status); - }); - test('asserts that a parser was given', function() { - assert.throws(function() { string('x').skip('not a parser'); }); - }); - }); - - suite('fallback', function() { - test('allows fallback result if no match is found', function() { - var parser = string('a').fallback('nothing'); - - assert.deepEqual(parser.parse('a').value, 'a'); - assert.deepEqual(parser.parse('').value, 'nothing'); - }); - }); - - suite('or', function() { - test('two parsers', function() { - var parser = string('x').or(string('y')); - - assert.equal(parser.parse('x').value, 'x'); - assert.equal(parser.parse('y').value, 'y'); - assert.ok(!parser.parse('z').status); - }); - - test('with chain', function() { - var parser = string('\\') - .chain(function() { - return string('y'); - }).or(string('z')); - - assert.equal(parser.parse('\\y').value, 'y'); - assert.equal(parser.parse('z').value, 'z'); - assert.ok(!parser.parse('\\z').status); - }); - test('asserts that a parser was given', function() { - assert.throws(function() { string('x').or('not a parser'); }); - }); - }); - - suite('many', function() { - test('simple case', function() { - var letters = letter.many(); - - assert.deepEqual(letters.parse('x').value, ['x']); - assert.deepEqual(letters.parse('xyz').value, ['x','y','z']); - assert.deepEqual(letters.parse('').value, []); - assert.ok(!letters.parse('1').status); - assert.ok(!letters.parse('xyz1').status); - }); - - test('followed by then', function() { - var parser = string('x').many().then(string('y')); - - assert.equal(parser.parse('y').value, 'y'); - assert.equal(parser.parse('xy').value, 'y'); - assert.equal(parser.parse('xxxxxy').value, 'y'); - }); - }); - - suite('times', function() { - test('zero case', function() { - var zeroLetters = letter.times(0); - - assert.deepEqual(zeroLetters.parse('').value, []); - assert.ok(!zeroLetters.parse('x').status); - }); - - test('nonzero case', function() { - var threeLetters = letter.times(3); - - assert.deepEqual(threeLetters.parse('xyz').value, ['x', 'y', 'z']); - assert.ok(!threeLetters.parse('xy').status); - assert.ok(!threeLetters.parse('xyzw').status); - - var thenDigit = threeLetters.then(digit); - assert.equal(thenDigit.parse('xyz1').value, '1'); - assert.ok(!thenDigit.parse('xy1').status); - assert.ok(!thenDigit.parse('xyz').status); - assert.ok(!thenDigit.parse('xyzw').status); - }); - - test('with a min and max', function() { - var someLetters = letter.times(2, 4); - - assert.deepEqual(someLetters.parse('xy').value, ['x', 'y']); - assert.deepEqual(someLetters.parse('xyz').value, ['x', 'y', 'z']); - assert.deepEqual(someLetters.parse('xyzw').value, ['x', 'y', 'z', 'w']); - assert.ok(!someLetters.parse('xyzwv').status); - assert.ok(!someLetters.parse('x').status); - - var thenDigit = someLetters.then(digit); - assert.equal(thenDigit.parse('xy1').value, '1'); - assert.equal(thenDigit.parse('xyz1').value, '1'); - assert.equal(thenDigit.parse('xyzw1').value, '1'); - assert.ok(!thenDigit.parse('xy').status); - assert.ok(!thenDigit.parse('xyzw').status); - assert.ok(!thenDigit.parse('xyzwv1').status); - assert.ok(!thenDigit.parse('x1').status); - }); - - test('atMost', function() { - var atMostTwo = letter.atMost(2); - assert.deepEqual(atMostTwo.parse('').value, []); - assert.deepEqual(atMostTwo.parse('a').value, ['a']); - assert.deepEqual(atMostTwo.parse('ab').value, ['a', 'b']); - assert.ok(!atMostTwo.parse('abc').status); - }); - - test('atLeast', function() { - var atLeastTwo = letter.atLeast(2); - - assert.deepEqual(atLeastTwo.parse('xy').value, ['x', 'y']); - assert.deepEqual(atLeastTwo.parse('xyzw').value, ['x', 'y', 'z', 'w']); - assert.ok(!atLeastTwo.parse('x').status); - }); - test('checks that argument types are correct', function() { - assert.throws(function() { string('x').times('not a number'); }); - assert.throws(function() { string('x').times(1, 'not a number'); }); - assert.throws(function() { string('x').atLeast('not a number'); }); - assert.throws(function() { string('x').atMost('not a number'); }); - }); - }); - - suite('fail', function() { - var fail = Parsimmon.fail; - var succeed = Parsimmon.succeed; - - test('use Parsimmon.fail to fail dynamically', function() { - var parser = any.chain(function(ch) { - return fail('a character besides ' + ch); - }).or(string('x')); - - assert.deepEqual(parser.parse('y'), { - status: false, - index: { - offset: 1, - line: 1, - column: 2 - }, - expected: ['a character besides y'] - }); - assert.equal(parser.parse('x').value, 'x'); - }); - - test('use Parsimmon.succeed or Parsimmon.fail to branch conditionally', function() { - var allowedOperator; - - var parser = - string('x') - .then(string('+').or(string('*'))) - .chain(function(operator) { - if (operator === allowedOperator) return succeed(operator); - else return fail(allowedOperator); - }) - .skip(string('y')) - ; - - allowedOperator = '+'; - assert.equal(parser.parse('x+y').value, '+'); - assert.deepEqual(parser.parse('x*y'), { - status: false, - index: { - offset: 2, - line: 1, - column: 3 - }, - expected: ['+'] - }); - - allowedOperator = '*'; - assert.equal(parser.parse('x*y').value, '*'); - assert.deepEqual(parser.parse('x+y'), { - status: false, - index: { - offset: 2, - line: 1, - column: 3 - }, - expected: ['*'] - }); - }); - }); - - test('eof', function() { - var parser = optWhitespace.skip(eof).or(all.result('default')); - - assert.equal(parser.parse(' ').value, ' '); - assert.equal(parser.parse('x').value, 'default'); - }); - - test('test', function() { - var parser = Parsimmon.test(function(ch) { return ch !== '.'; }); - assert.equal(parser.parse('x').value, 'x'); - assert.equal(parser.parse('.').status, false); - assert.throws(function() { Parsimmon.test('not a function'); }); - }); - - test('takeWhile', function() { - var parser = Parsimmon.takeWhile(function(ch) { return ch !== '.'; }) - .skip(all); - assert.equal(parser.parse('abc').value, 'abc'); - assert.equal(parser.parse('abc.').value, 'abc'); - assert.equal(parser.parse('.').value, ''); - assert.equal(parser.parse('').value, ''); - assert.throws(function() { Parsimmon.takeWhile('not a function'); }); - }); - - test('index', function() { - var parser = regex(/^[x\n]*/).then(index); - assert.deepEqual(parser.parse('').value, { - offset: 0, - line: 1, - column: 1 - }); - assert.deepEqual(parser.parse('xx').value, { - offset: 2, - line: 1, - column: 3 - }); - assert.deepEqual(parser.parse('xx\nxx').value, { - offset: 5, - line: 2, - column: 3 - }); - }); - - test('mark', function() { - var ys = regex(/^y*/).mark(); - var parser = optWhitespace.then(ys).skip(optWhitespace); - assert.deepEqual( - parser.parse('').value, - { - value: '', - start: {offset: 0, line: 1, column: 1}, - end: {offset: 0, line: 1, column: 1} - } - ); - assert.deepEqual( - parser.parse(' yy ').value, - { - value: 'yy', - start: {offset: 1, line: 1, column: 2}, - end: {offset: 3, line: 1, column: 4} - } - ); - assert.deepEqual( - parser.parse('\nyy ').value, - { - value: 'yy', - start: {offset: 1, line: 2, column: 1}, - end: {offset: 3, line: 2, column: 3} - } - ); - }); - - test('.node(name)', function() { - var ys = regex(/^y*/).node('Y'); - var parser = optWhitespace.then(ys).skip(optWhitespace); - assert.deepEqual( - parser.parse('').value, - { - name: 'Y', - value: '', - start: {offset: 0, line: 1, column: 1}, - end: {offset: 0, line: 1, column: 1} - } - ); - assert.deepEqual( - parser.parse(' yy ').value, - { - name: 'Y', - value: 'yy', - start: {offset: 1, line: 1, column: 2}, - end: {offset: 3, line: 1, column: 4} - } - ); - assert.deepEqual( - parser.parse('\nyy ').value, - { - name: 'Y', - value: 'yy', - start: {offset: 1, line: 2, column: 1}, - end: {offset: 3, line: 2, column: 3} - } - ); - }); - - suite('smart error messages', function() { - // this is mainly about .or(), .many(), and .times(), but not about - // their core functionality, so it's in its own test suite - - suite('or', function() { - test('prefer longest branch', function() { - var parser = string('abc').then(string('def')).or(string('ab').then(string('cd'))); - - assert.deepEqual(parser.parse('abc'), { - status: false, - index: { - offset: 3, - line: 1, - column: 4 - }, - expected: ['\'def\''] - }); - }); - - test('prefer last of equal length branches', function() { - var parser = string('abc').then(string('def')).or(string('abc').then(string('d'))); - - assert.deepEqual(parser.parse('abc'), { - status: false, - index: { - offset: 3, - line: 1, - column: 4 - }, - expected: ['\'d\'', '\'def\''] - }); - }); - - test('prefer longest branch even after a success', function() { - var parser = string('abcdef').then(string('g')).or(string('ab')) - .then(string('cd')).then(string('xyz')); - - assert.deepEqual(parser.parse('abcdef'), { - status: false, - index: { - offset: 6, - line: 1, - column: 7 - }, - expected: ['\'g\''] - }); - }); - }); - - suite('many', function() { - test('prefer longest branch even in a .many()', function() { - var list = lazy(function() { - return optWhitespace.then(atom.or(sexpr)).skip(optWhitespace).many(); - }); - var atom = regex(/[^()\s]+/).desc('an atom'); - var sexpr = string('(').then(list).skip(string(')')); - - assert.deepEqual( - list.parse('(a b) (c ((() d)))').value, - [['a', 'b'], ['c', [[[], 'd']]]] - ); - - assert.deepEqual(list.parse('(a b ()) c)'), { - status: false, - index: { - offset: 10, - line: 1, - column: 11 - }, - expected: ['\'(\'', 'EOF', 'an atom'] - }); - - assert.deepEqual(list.parse('(a (b)) (() c'), { - status: false, - index: { - offset: 13, - line: 1, - column: 14 - }, - expected: ['\'(\'', '\')\'', 'an atom'] - }); - }); - - test('prefer longest branch in .or() nested in .many()', function() { - var parser = string('abc').then(string('def')).or(string('a')).many(); - - assert.deepEqual(parser.parse('aaabcdefaa').value, - ['a', 'a', 'def', 'a', 'a']); - - assert.deepEqual(parser.parse('aaabcde'), { - status: false, - index: { - offset: 5, - line: 1, - column: 6 - }, - expected: ['\'def\''] - }); - }); - }); - - suite('times', function() { - test('prefer longest branch in .times() too', function() { - var parser = string('abc').then(string('def')).or(string('a')).times(3, 6); - - assert.deepEqual(parser.parse('aabcde'), { - status: false, - index: { - offset: 4, - line: 1, - column: 5 - }, - expected: ['\'def\''] - }); - - assert.deepEqual(parser.parse('aaaaabcde'), { - status: false, - index: { - offset: 7, - line: 1, - column: 8 - }, - expected: ['\'def\''] - }); - }); - }); - - suite('desc', function() { - test('allows custom error messages', function() { - var x = string('x').desc('the letter x'); - var y = string('y').desc('the letter y'); - var parser = x.then(y); - - assert.deepEqual(parser.parse('a'), { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['the letter x'] - }); - - assert.deepEqual(parser.parse('xa'), { - status: false, - index: { - offset: 1, - line: 1, - column: 2 - }, - expected: ['the letter y'] - }); - }); - - test('allows tagging with `lazy`', function() { - var x = lazy('the letter x', function() { return string('x'); }); - var y = lazy('the letter y', function() { return string('y'); }); - var parser = x.then(y); - - assert.deepEqual(parser.parse('a'), { - status: false, - index: { - offset: 0, - line: 1, - column: 1 - }, - expected: ['the letter x'] - }); - - assert.deepEqual(parser.parse('xa'), { - status: false, - index: { - offset: 1, - line: 1, - column: 2 - }, - expected: ['the letter y'] - }); - }); - }); - }); -}); diff --git a/test/setup.js b/test/setup.js new file mode 100644 index 0000000..96d82a1 --- /dev/null +++ b/test/setup.js @@ -0,0 +1,4 @@ +'use strict'; + +global.assert = require('assert'); +global.Parsimmon = require('..'); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..084d3c6 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,18 @@ +/* eslint-env node */ + +'use strict'; + +// Let's offer a minified UMD build of Parsimmon, while keeping the main +// library as a Node.js module. + +var path = require('path'); + +module.exports = { + entry: './src/parsimmon.js', + output: { + path: path.resolve(__dirname, 'build'), + library: 'Parsimmon', + libraryTarget: 'umd', + filename: 'parsimmon.umd.min.js' + } +};