{"id":15629715,"url":"https://github.com/anko/partser","last_synced_at":"2025-10-28T16:44:10.525Z","repository":{"id":5523734,"uuid":"53368679","full_name":"anko/partser","owner":"anko","description":"combinatory parsing library with hot-swappable parts and nested environments","archived":false,"fork":false,"pushed_at":"2023-01-09T22:54:55.000Z","size":561,"stargazers_count":8,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-02T21:07:22.325Z","etag":null,"topics":["hot-swap","javascript","modular","parser-library","programmable","self-modifying-code"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anko.png","metadata":{"files":{"readme":"readme.markdown","changelog":"history.markdown","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-08T00:12:32.000Z","updated_at":"2025-02-07T09:45:52.000Z","dependencies_parsed_at":"2023-01-13T13:35:03.187Z","dependency_job_id":null,"html_url":"https://github.com/anko/partser","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/anko/partser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anko%2Fpartser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anko%2Fpartser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anko%2Fpartser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anko%2Fpartser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anko","download_url":"https://codeload.github.com/anko/partser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anko%2Fpartser/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263234943,"owners_count":23434918,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hot-swap","javascript","modular","parser-library","programmable","self-modifying-code"],"created_at":"2024-10-03T10:28:16.458Z","updated_at":"2025-10-28T16:44:05.431Z","avatar_url":"https://github.com/anko.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# partser [![](https://img.shields.io/npm/v/partser.svg?style=flat-square)](https://www.npmjs.com/package/partser) [![](https://img.shields.io/travis/anko/partser.svg?style=flat-square)](https://travis-ci.org/anko/partser) ![](https://img.shields.io/librariesio/release/npm/partser?style=flat-square) [![](https://img.shields.io/coveralls/github/anko/partser?style=flat-square)](https://coveralls.io/github/anko/partser)\n\nA combinatory parsing library for JS, for writing LL(∞) parsers made of other\nparsers.\n\nIt is *ridiculously flexible*: Your parsers can modify their parsing logic even\nduring parsing, by introducing, redefining, or modifying sub-parsers inside\nnested scoped environments, even based on partial parse results.\n\n## Example\n\nHere's a demonstration of a string literal parser that reads the quote symbol\nthat it should use from the environment object passed by the caller:\n\n\u003c!-- !test program node test-readme-example.js --\u003e\n\n\u003c!-- !test in motivating example --\u003e\n\n``` js\nconst p = require('partser')\n\n// Let's parse a string!\n\n// For fun, let's load the quote character from the parse environment.\nconst quote = p.from((env) =\u003e env.quoteParser)\n\n// The string can contain any characters that aren't quotes.\nconst stringChar = p.except(p.any, quote)\n\n// The contents of a string (the stuff between quotes) shall be many\n// stringChars, joined together.\nconst stringContents = p.map(\n  p.times(stringChar, 0, Infinity),\n  (chars) =\u003e chars.join(''))\n\n// A string is a quote, string contents, then another quote.\n// We'll pick out just the content part, and return that.\nconst stringParser = p.map(\n  p.seq([quote, stringContents, quote]),\n  ([openingQuote, contents, closingQuote]) =\u003e contents)\n\n// Now we can pass an environment object when calling the parser, to specify\n// what quote character should be used.\nconsole.log(stringParser('\"hi\"', { quoteParser: p.string('\"') }))\nconsole.log(stringParser('$hi$', { quoteParser: p.string('$') }))\nconsole.log(stringParser('ohio', { quoteParser: p.string('o') }))\n```\n\nOutput:\n\n\u003c!-- !test out motivating example --\u003e\n\n\u003e ```\n\u003e { status: true, index: 4, value: 'hi' }\n\u003e { status: true, index: 4, value: 'hi' }\n\u003e { status: true, index: 4, value: 'hi' }\n\u003e ```\n\nFor sub-environments, see the [`p.subEnv` example\nbelow](#psubenvparser-derivefunction).\n\n## Usage\n\nPartser gives you functions of a few different types:\n\n - [*primitive parsers*](#primitive-parsers) that consume strings and return\n   tokens (e.g. `all` or `any`),\n - [*parser constructors*](#parser-constructors) that create new parsers based\n   on arguments (e.g.  `string` or `regex`),\n - [*parser combinators*](#parser-combinators) that take parsers and produce\n   new parsers that use them (e.g.  `seq`, `alt`, or `map`),\n - [*helper functions*](#helper-functions), for debugging, error-formatting,\n   and other miscellaneous related tasks.\n\nTogether these can be used to express how to turn text into a data structure.\n\n### Calling a parser\n\n    parser(input [, environment [, offset]])\n\n - `input` (`String`): the string to parse from\n - `environment` (`(any type)`; *optional*): environment object passed to other\n   parsers, and to user-defined functions such as in the `map` parser (default:\n   `undefined`)\n - `offset` (`Number`; *optional*): integer character offset for where in\n   `input` to start parsing (default: 0)\n\nReturns:\n\n—on success:\n\n - `status` (`Boolean`): `true`\n - `value`: the return value of the parse\n - `index` (`Number`): how many characters were consumed\n\n —on failure:\n\n - `status` (`Boolean`): `false`\n - `value` (`Array`): human-readable strings representing what input would have\n   been acceptable instead\n - `index` (`Number`): the offset at which the parse encountered a dead end\n\n### Primitive parsers\n\nThese parsers are already pre-defined for you:\n\n#### `p.all`\n\nAlways succeeds, consuming all input and returning it.\n\n\u003c!-- !test in all --\u003e\n\n    const parser = p.all\n    console.log(parser('ashldflasdhfl'))\n\n\u003c!-- !test out all --\u003e\n\n\u003e ```\n\u003e { status: true, index: 13, value: 'ashldflasdhfl' }\n\u003e ```\n\n#### `p.any`\n\nMatches any 1 character and returns it.\n\n\u003c!-- !test in any --\u003e\n\n    const parser = p.any\n    console.log(parser('a'))\n    console.log(parser('b'))\n\n\u003c!-- !test out any --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: 'a' }\n\u003e { status: true, index: 1, value: 'b' }\n\u003e ```\n\n#### `p.eof`\n\nMatches the end of input (only matches if no more characters are remaining) and\nreturns null.\n\n\u003c!-- !test in eof --\u003e\n\n    const parser = p.eof\n    console.log(parser(''))\n\n\u003c!-- !test out eof --\u003e\n\n\u003e ```\n\u003e { status: true, index: 0, value: null }\n\u003e ```\n\n#### `p.index`\n\nAlways succeeds, without consuming any input.  Returns a 0-based integer\nrepresenting the offset into the input that has been consumed so far.\n\n\u003c!-- !test in index --\u003e\n\n    const parser = p.seq([\n      p.string('hi'),\n      p.index\n    ])\n    console.log(parser('hi'))\n\n\u003c!-- !test out index --\u003e\n\n\u003e ```\n\u003e { status: true, index: 2, value: [ 'hi', 2 ] }\n\u003e ```\n\n#### `p.lcIndex`\n\nAlways succeeds, without consuming any input.  Returns an object with integer\nfields `line` (1-based), `column` (1-based) and character `offset` (0-based),\nwhich represents how much input has been consumed so far.\n\nThis is a more verbose version of [`p.index`](#pindex).  For performance, use\nthat if you only need the character offset.\n\n\u003c!-- !test in lcIndex --\u003e\n\n    const parser = p.seq([\n      p.string('hi'),\n      p.lcIndex\n    ])\n    console.log(parser('hi'))\n\n\u003c!-- !test out lcIndex --\u003e\n\n\u003e ```\n\u003e {\n\u003e   status: true,\n\u003e   index: 2,\n\u003e   value: [ 'hi', { offset: 2, line: 1, column: 3 } ]\n\u003e }\n\u003e ```\n\n### Parser constructors\n\nThese functions let you construct your own parsers that match various things:\n\n#### `p.succeed([value])`\n\nReturn:  Parser that always succeeds with `value` or undefined, without\nconsuming any input.\n\n\u003c!-- !test in succeed --\u003e\n\n    const parser = p.succeed('success!')\n    console.log(parser(''))\n\n\u003c!-- !test out succeed --\u003e\n\n\u003e ```\n\u003e { status: true, index: 0, value: 'success!' }\n\u003e ```\n\n#### `p.fail([value])`\n\nReturn:  Parser that always fails with `value` or undefined, without consuming\nany input.\n\n\u003c!-- !test in fail --\u003e\n\n    const parser = p.fail('failure!')\n    console.log(parser(''))\n\n\u003c!-- !test out fail --\u003e\n\n\u003e ```\n\u003e { status: false, index: 0, value: [ 'failure!' ] }\n\u003e ```\n\n#### `p.string(value:String)`\n\nReturn:  Parser that matches that string and returns it.\n\n\u003c!-- !test in string --\u003e\n\n    const parser = p.string('Hello!')\n    console.log(parser('Hello!'))\n\n\u003c!-- !test out string --\u003e\n\n\u003e ```\n\u003e { status: true, index: 6, value: 'Hello!' }\n\u003e ```\n\n#### `p.regex(regex:RegExp [, group:Number])`\n\nReturn:  Parser that matches the given `regex` and returns the given capturing `group` (default: 0).\n\n\u003c!-- !test in regex --\u003e\n\n    const parser = p.regex(/ok(ay)?/)\n    console.log(parser('okay'))\n\n\u003c!-- !test out regex --\u003e\n\n\u003e ```\n\u003e { status: true, index: 4, value: 'okay' }\n\u003e ```\n\n#### `p.test(predicate:Function)`\n\nReturn:  Parser that consumes 1 `character`, calls `predicate(character, env)`.\nSucceeds and returns `character` if `predicate` returns true.  Otherwise fails.\n\nNice for when you need to do math on character values, like checking Unicode\ncharacter ranges.\n\n\u003c!-- !test in text --\u003e\n\n    const parser = p.test((x) =\u003e x.charCodeAt(0) \u003c 100)\n    console.log(parser('0')) // character code 48\n    console.log(parser('x')) // character code 120\n\n\u003c!-- !test out text --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: '0' }\n\u003e {\n\u003e   status: false,\n\u003e   index: 0,\n\u003e   value: [ 'a character matching (x) =\u003e x.charCodeAt(0) \u003c 100' ]\n\u003e }\n\u003e ```\n\n#### `p.custom(implementation:Function)`\n\nReturn:  Parser that works according to the logic specified in the given\n`implementation`.  The `implementation` should have the API demonstrated in the\nbelow example.\n\nThis function wraps your custom parser functionality such that it works with\nall other parsers and combinator functions.\n\n\u003c!-- !test in custom --\u003e\n\n    const otherParser = p.string('x')\n\n    const parser = p.custom((input, index, env, debugHandler) =\u003e {\n      // Put whatever logic you want here.\n\n      // You don't have to call any methods in `debugHandler`.  It's done\n      // automatically for you.  You should however pass it on when calling\n      // other parsers, if you want `p.debug` to be able to display them.\n\n      if (input[index] === 'a') {\n        // If you want to call another parser, call its `_` property.  This\n        // makes the parser succeed if it matches, even if it didn't consume\n        // all of the input.\n        const xResult = otherParser._(input, index + 1, env, debugHandler)\n        if (xResult.status === false) return xResult\n\n        return {\n          status: true,\n          index: xResult.index,\n          value: { word: 'a' + xResult.value, greeting: env.hello }\n        }\n      } else {\n        return {\n          status: false,\n          index,\n          value: ['the letter a']\n        }\n      }\n    })\n\n    console.log(parser('b'))\n    console.log(parser('a'))\n    console.log(parser('ax', { hello: 'hi' }))\n\n\u003c!-- !test out custom --\u003e\n\n\u003e ```\n\u003e { status: false, index: 0, value: [ 'the letter a' ] }\n\u003e { status: false, index: 1, value: [ \"'x'\" ] }\n\u003e { status: true, index: 2, value: { word: 'ax', greeting: 'hi' } }\n\u003e ```\n\n### Parser combinators\n\nThese functions operate on parsers, acting as \"wrappers\" around them to modify\nhow they work.\n\n#### `p.seq(parsers [, chainEnv])`\n\nReturn:  Parser that matches all of the given `parser`s in order, and returns\nan Array of their results.\n\n\u003c!-- !test in seq --\u003e\n\n    const parser = p.seq([\n      p.string('a'),\n      p.regex(/[xyz]/)\n    ])\n\n    console.log(parser('ax'))\n\n\u003c!-- !test out seq --\u003e\n\n\u003e ```\n\u003e { status: true, index: 2, value: [ 'a', 'x' ] }\n\u003e ```\n\n\nThe `chainEnv` argument can be passed a function to define how environments are\npassed forward through a sequence of parsers.  [See guidance\nbelow](#passing-the-environment-through-a-parser-sequence).\n\n\n#### `p.alt(parsers)`\n\nReturns a parser that matches any of the given `parsers`, and returns the\nresult of the first one that matched.\n\n\u003c!-- !test in alt --\u003e\n\n    const parser = p.alt([\n      p.string('a'),\n      p.string('b')])\n\n    console.log(parser('b'))\n\n\u003c!-- !test out alt --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: 'b' }\n\u003e ```\n\n#### `p.times(parser, min:Number [, max:Number] [, chainEnv:Function])`\n\nReturns a parser that matches the given `parser` at least `min`, and at most\n`max` times, and returns an Array of the results.\n\nIf `max` is not given, `max = min`.\n\n\u003c!-- !test in times --\u003e\n\n    const parser = p.times(p.string('A'), 2, Infinity)\n\n    console.log(parser('A'))\n    console.log(parser('AA'))\n    console.log(parser('AAAAA'))\n\n\u003c!-- !test out times --\u003e\n\n\u003e ```\n\u003e { status: false, index: 1, value: [ \"'A'\" ] }\n\u003e { status: true, index: 2, value: [ 'A', 'A' ] }\n\u003e { status: true, index: 5, value: [ 'A', 'A', 'A', 'A', 'A' ] }\n\u003e ```\n\nThe `chainEnv` argument can be passed a function to define how environments are\npassed forward through a sequence of parsers.  [See guidance\nbelow](#passing-the-environment-through-a-parser-sequence).\n\n#### `p.except(allowedParser, forbiddenParser)`\n\nReturns a parser that matches what `allowedParser` matches, except if what it\nmatched would also match `forbiddenParser`.\n\n\u003c!-- !test in except --\u003e\n\n    const parser = p.except(p.regex(/[a-z]/), p.string('b'))\n\n    console.log(parser('a'))\n    console.log(parser('b'))\n    console.log(parser('c'))\n\n\u003c!-- !test out except --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: 'a' }\n\u003e { status: false, index: 0, value: [ \"something that is not 'b'\" ] }\n\u003e { status: true, index: 1, value: 'c' }\n\u003e ```\n\n#### `p.desc(parser, description:String)`\n\nReturns a parser that works exactly the same as `parser`, but always fails with\nthe `description` as its expected value.\n\nUseful for making complex parsers show clearer error messages.\n\n\u003c!-- !test in desc --\u003e\n\n    const floatParser = p.map(\n      p.seq([p.regex(/[0-9]+/), p.string('.'), p.regex(/[0-9]+/)]),\n      ([left, dot, right]) =\u003e {\n        return { left: Number(left), right: Number(right) }\n      })\n    const parser = p.desc(floatParser, 'a float constant')\n\n    console.log(parser('3.2'))\n    console.log(parser('1'))\n\n\u003c!-- !test out desc --\u003e\n\n\u003e ```\n\u003e { status: true, index: 3, value: { left: 3, right: 2 } }\n\u003e { status: false, index: 1, value: [ 'a float constant' ] }\n\u003e ```\n\n#### `p.mark(parser)`\n\nReturns a parser that works exactly like `parser`, but when it succeeds, it\nannotates the return `value` with the `start` and `end` offsets of where that\nvalue was found.  The `value` becomes an Object with `{ value, start, end }`\ninstead.\n\nUseful when you need to know not only that something matched, but *where* it\nwas matched, such as for generating a [source\nmap](https://github.com/mozilla/source-map).\n\n\u003c!-- !test in mark --\u003e\n\n    const parser = p.mark(p.string('abc'))\n\n    console.log(parser('abc'))\n\n\u003c!-- !test out mark --\u003e\n\n\u003e ```\n\u003e { status: true, index: 3, value: { start: 0, value: 'abc', end: 3 } }\n\u003e ```\n\n#### `p.lcMark(parser)`\n\nLike [`p.mark`](#pmarkparser), but also annotates the value with 1-based `line` and\n`column` locations.\n\n\u003c!-- !test in lcMark --\u003e\n\n    const parser = p.lcMark(p.string('abc'))\n\n    console.log(parser('abc'))\n\n\u003c!-- !test out lcMark --\u003e\n\n\u003e ```\n\u003e {\n\u003e   status: true,\n\u003e   index: 3,\n\u003e   value: {\n\u003e     start: { offset: 0, line: 1, column: 1 },\n\u003e     value: 'abc',\n\u003e     end: { offset: 3, line: 1, column: 4 }\n\u003e   }\n\u003e }\n\u003e ```\n\n#### `p.map(parser, transformer:Function)`\n\nReturns a parser that works exactly like `parser`, but when it succeeds with a\n`value`, it instead returns `transformer(value, env)`.\n\nAnalogous to\n[`Array.prototype.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).\n\n\u003c!-- !test in map --\u003e\n\n    const parser = p.map(p.regex(/[0-9]+/), (x) =\u003e 2 * Number(x))\n\n    console.log(parser('21'))\n\n\u003c!-- !test out map --\u003e\n\n\u003e ```\n\u003e { status: true, index: 2, value: 42 }\n\u003e ```\n\n#### `p.chain(parser, decider:Function)`\n\nReturns a parser that matches the given `parser` to get a `value`, then calls\n`decider(value, env)` expecting it to return a parser.  Then matches and\nreturns *that parser* returned by `decider`.\n\n:warning: *You almost certainly want [`p.from`](#pfromdecideparserfunction) instead.*  This is a classic\ncombinator possibly familiar to users of other parsing libraries.  I've\nimplemented it here mainly to reduce the cognitive load of porting parsers\nbetween libraries.\n\n\u003c!-- !test in chain --\u003e\n\n    const parser = p.chain(p.regex(/[ax]/), (x) =\u003e {\n      if (x === 'a') return p.string('bc')\n      else return p.string('yz')\n    })\n\n    console.log(parser('abc'))\n    console.log(parser('xyz'))\n    console.log(parser('ayz'))\n\n\u003c!-- !test out chain --\u003e\n\n\u003e ```\n\u003e { status: true, index: 3, value: 'bc' }\n\u003e { status: true, index: 3, value: 'yz' }\n\u003e { status: false, index: 1, value: [ \"'bc'\" ] }\n\u003e ```\n\n#### `p.subEnv(parser, derive:Function)`\n\nReturns a parser that works exactly like the given `parser`, but with a\ndifferent environment object passed to its parsers.  The new environment object\nis created by calling `derive(env)` where `env` is the current environment.\n\n\u003c!-- !test in subEnv --\u003e\n\n    const env = { level: 0 }\n\n    const expression = p.from(() =\u003e p.alt([listParser, dotParser]))\n    const dotParser = p.map(p.string('.'), (value, env) =\u003e env)\n    const listParser = p.subEnv(\n      p.map(\n        p.seq([\n          p.string('('),\n          p.times(expression, 0, Infinity),\n          p.string(')')\n        ]),\n        ([leftParen, value, rightParen]) =\u003e value),\n      (env) =\u003e ({ level: env.level + 1 }))\n\n    console.log(expression('.', env))\n    console.log(expression('(.)', env))\n    console.log(expression('((.).)', env))\n\n\u003c!-- !test out subEnv --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: { level: 0 } }\n\u003e { status: true, index: 3, value: [ { level: 1 } ] }\n\u003e { status: true, index: 6, value: [ [ { level: 2 } ], { level: 1 } ] }\n\u003e ```\n\n#### `p.from(decideParser:Function)`\n\nDelegates to the parser returned by `decideParser(environment)`.\n\nThis lets you decide dynamically in the middle of parsing what you want this\nparser to be, based on the `environment`, or otherwise.\n\n\u003c!-- !test in from --\u003e\n\n    const parser = p.from((env) =\u003e env.myParser)\n\n    console.log(parser('abc', { myParser: p.string('abc') }))\n    console.log(parser('something else', { myParser: p.all }))\n\n\u003c!-- !test out from --\u003e\n\n\u003e ```\n\u003e { status: true, index: 3, value: 'abc' }\n\u003e { status: true, index: 14, value: 'something else' }\n\u003e ```\n\n#### `p.clone(parser)`\n\nReturns a parser that works exactly like the given `parser`, but has a distinct\nobject identity.\n\nIt may be useful if you're intending to\n[`p.replace`](#preplacetargetparser-sourceparser) the original and want a copy\nthat doesn't change to point to its new `p.replace`d implementation.\n\n:warning: This is a hack that may be useful for debugging, but which you\nprobably shouldn't use in actual code.  It is almost certainly better\narchitecture to simply create a function that can construct copies of the\nidentical parser you need, or just pass the same parser to multiple places.\nSee the warning on [`p.replace`](#preplacetargetparser-sourceparser) for more\nabout this.\n\n\u003c!-- !test in clone --\u003e\n\n    const parser = p.string('a')\n    const clonedParser = p.clone(parser)\n    p.replace(parser, p.string('b'))\n\n    console.log(parser('b'))\n    console.log(clonedParser('a'))\n    console.log(clonedParser('b'))\n\n\u003c!-- !test out clone --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: 'b' }\n\u003e { status: true, index: 1, value: 'a' }\n\u003e { status: false, index: 0, value: [ \"'a'\" ] }\n\u003e ```\n\n### Helper functions\n\n#### `p.debug(parser [, debugHandler:Object])`\n\nReturns a parser that works identically to the given `parser`, but with debug\ninstrumentation.\n\nIf a `debugHandler` is passed, its properties are called as functions during\nparsing:\n\n - `debugHandler.enter` is called before a parser executes with arguments\n   `parser:Parser`, `input:String`, `index:Number`, `env: Any`.\n - `debugHandler.exit` is called once a parser returns, with the same arguments\n   plus `result:Object` in [the same format as\n   normally](https://github.com/anko/partser#calling-a-parser).\n\nWithout a custom debug handler given, the default is used, which prints a\ncoloured visualisation of the parse:\n\n\u003c!-- !test in debug --\u003e\n\n```\nconst parser = p.times(\n  p.alt([p.string('ba'), p.string('na')]),\n  0, 3)\nconst parserWithDebug = p.debug(parser)\nconst result = parserWithDebug('banana')\nconsole.log(result)\n```\n\nWith colour support:\n\n\u003e ![debug output with colours](https://user-images.githubusercontent.com/5231746/94251806-5ef0d080-ff23-11ea-8e3f-f59aa74dc51c.png)\n\nWithout colour:\n\n\u003c!-- !test out debug --\u003e\n\n\u003e ```\n\u003e banana 1,1 times(0,3) ?\n\u003e banana · 1,1 alt(*2) ?\n\u003e banana · · 1,1 string(\"ba\") ?\n\u003e banana · · 1,1 string(\"ba\") OKAY \"ba\" (len 2)\n\u003e banana · 1,1 alt(*2) OKAY \"ba\" (len 2)\n\u003e banana · 1,3 alt(*2) ?\n\u003e banana · · 1,3 string(\"ba\") ?\n\u003e banana · · 1,3 string(\"ba\") FAIL [\"'ba'\"]\n\u003e banana · · 1,3 string(\"na\") ?\n\u003e banana · · 1,3 string(\"na\") OKAY \"na\" (len 2)\n\u003e banana · 1,3 alt(*2) OKAY \"na\" (len 2)\n\u003e banana · 1,5 alt(*2) ?\n\u003e banana · · 1,5 string(\"ba\") ?\n\u003e banana · · 1,5 string(\"ba\") FAIL [\"'ba'\"]\n\u003e banana · · 1,5 string(\"na\") ?\n\u003e banana · · 1,5 string(\"na\") OKAY \"na\" (len 2)\n\u003e banana · 1,5 alt(*2) OKAY \"na\" (len 2)\n\u003e banana 1,1 times(0,3) OKAY \"banana\" (len 6)\n\u003e { status: true, index: 6, value: [ 'ba', 'na', 'na' ] }\n\u003e ```\n\n:warning: The output of the default debug handler is intended for human\ninterpretation.  It may change in the future.  If you want to consume debug\ninformation programmatically, create your own debug handler.\n\n#### `p.debug.makeHandler([options:Object])`\n\nCreates a debug handler similar to the default, but with configurable\n`options`:\n\n - `context:Number`: Number of chars of input to show at the left for context\n   (default: `10`)\n\n - `padIfShort:Boolean`: Set this to `true` to pad the context strip to the\n   same length if the input string doesn't fill it completely (default:\n   `false`)\n\n - `enter:Function`: Is passed the same parameters as the `enter` property of\n   the debug handler.\n\n   If it returns `false`, this log entry is skipped.\n\n   If it returns some truthy value, that value is appended to the regular log\n   entry as extra data.  Any extra data will be indented appropriately and\n   placed after the usual debug print.\n\n   (default: `undefined`; no extra data shown)\n\n - `exit:Function`: As above, but for `exit`.  (default: `undefined`; no extra\n   data shown)\n\nUse-cases for this function include displaying the parse environment in a\ndomain-appropriate way, and filtering which log entries are shown.\n\n\u003c!-- !test in debug with extra info --\u003e\n\n```js\nconst env = { wasCalled: false }\nconst parser = p.from((env) =\u003e {\n  env.wasCalled = true\n  return p.string('a')\n})\n\nconst debugHandler = p.debug.makeHandler({\n  context: 10,\n  enter: (name, input, index, env) =\u003e `→ ${env.wasCalled}`,\n  exit: (name, input, index, env, result) =\u003e `← ${env.wasCalled}`,\n  padIfShort: true\n})\nconst debugParser = p.debug(parser, debugHandler)\nconsole.log(debugParser('a', env))\n```\n\n\u003c!-- !test out debug with extra info --\u003e\n\n```\na          1,1 from ?\na          → false\na          · 1,1 string(\"a\") ?\na          · → true\na          · 1,1 string(\"a\") OKAY \"a\" (len 1)\na          · ← true\na          1,1 from OKAY \"a\" (len 1)\na          ← true\n{ status: true, index: 1, value: 'a' }\n```\n\nIf you want a completely different format, you can also create a custom handler\n(an object with `enter` and `exit` functions).  See `p.debug` for a description\nof the API.\n\n#### `p.replace(targetParser, sourceParser)`\n\nSwitches the `targetParser`'s parsing logic for the parsing logic of\n`sourceParser`, without affecting either's object identity.\n\nReturns `undefined`.\n\n:warning:  *This is a hack that you almost certainly shouldn't use.*  I keep it\naround because it's useful for debugging and unsafe duct-tape creativity.  If\nyou need to change parsers, you should probably implement them as\n[`p.from`](#pfromdecideparserfunction)s instead, and dynamically load the\ndesired implementation from your environment object.  That way you can use\n[`p.subEnv`](#psubenvparser-derivefunction)s too, to keep your parsing\nenvironments scoped and clean.  But the dirty large hammer is here if you need\nit for some reason.\n\n\u003c!-- !test in replace --\u003e\n\n    const parser = p.string('a')\n    p.replace(parser, p.string('b'))\n\n    console.log(parser('b'))\n\n\u003c!-- !test out replace --\u003e\n\n\u003e ```\n\u003e { status: true, index: 1, value: 'b' }\n\u003e ```\n\n#### `p.isParser(value)`\n\nReturns `true` if `value` is a Partser parser, and `false` otherwise.\n\n\u003c!-- !test in isParser --\u003e\n\n    const parser = p.string('a')\n    const someFunction = () =\u003e {}\n\n    console.log(p.isParser(parser))\n    console.log(p.isParser(someFunction))\n\n\u003c!-- !test out isParser --\u003e\n\n\u003e ```\n\u003e true\n\u003e false\n\u003e ```\n\n#### `p.formatError(input:String, result:Object)`\n\nTakes an `input` that you parsed, and the `result` of a failed parse of that\ninput.  Produces a human-readable error string stating what went wrong, where\nit went wrong, and what was expected instead.\n\nOutputs a basic human-readable error message which exact format is not\nguaranteed.  For production use, you should probably write your own error\nformatter, so you can have nice things like coloured output, and more context.\n\n\u003c!-- !test in formatError --\u003e\n\n    const parser = p.alt([p.string('a'), p.string('b')])\n\n    const input = 'c'\n    const result = parser(input)\n\n    console.log(p.formatError(input, result))\n\n\u003c!-- !test out formatError --\u003e\n\n\u003e ```\n\u003e expected one of 'b', 'a' at character 0, got 'c'\n\u003e ```\n\n## Tips and patterns\n\n### Recursive parsers\n\nTrying to make a recursive parser, or want to pass a not-yet-defined parser\nto a combinator, and getting a `ReferenceError`?  You can use\n[`p.from`](#pfromdecideparserfunction) to load it during parsing instead.\n\n\u003c!-- !test in using from to load later --\u003e\n\n```js\n// If we tried to pass `word` directly, we'd get an error like\n//\n//     ReferenceError: Cannot access 'word' before initialization\n//\nconst exclamation = p.seq([p.from(() =\u003e word), p.string('!')])\n\nconst word = p.regex(/\\w+/)\n\nconsole.log(exclamation('Hi!'))\n```\n\n\u003c!-- !test out using from to load later --\u003e\n\n\u003e ```\n\u003e { status: true, index: 3, value: [ 'Hi', '!' ] }\n\u003e ```\n\n### Make your own helper functions\n\nIt is frequently useful to create your own helper functions, to make your\nimplementation neater.\n\n\u003c!-- !test in helpers --\u003e\n\n```js\nconst node = (name, parser) =\u003e {\n  return p.map(\n    p.lcMark(parser),\n    (result) =\u003e Object.assign({ name }, result))\n}\n\nconst word = node('word', p.regex(/\\w+/))\nconst number = node('number', p.regex(/\\d+/))\n\nconsole.log(word('Hi').value)\nconsole.log(number('42').value)\n```\n\n\u003c!-- !test out helpers --\u003e\n\n\u003e ```\n\u003e {\n\u003e   name: 'word',\n\u003e   start: { offset: 0, line: 1, column: 1 },\n\u003e   value: 'Hi',\n\u003e   end: { offset: 2, line: 1, column: 3 }\n\u003e }\n\u003e {\n\u003e   name: 'number',\n\u003e   start: { offset: 0, line: 1, column: 1 },\n\u003e   value: '42',\n\u003e   end: { offset: 2, line: 1, column: 3 }\n\u003e }\n\u003e ```\n\n### Passing the environment through a parser sequence\n\nBy default, the same environment given to `p.seq`/`p.times` will be passed to\neach sequenced parser.\n\nWhen using sub-environments (`p.subEnv`), you may want some `p.seq`/`p.times`\nparsers to pass the repeated parser's modified sub-environment forward from\neach iteration to the next.  You can do this with the optional `chainEnv`\nargument.\n\nThe `chainEnv` argument should be a function.  It will be called with 2\nparameters:\n\n - `value`: A successful result of the previous parser.\n - `env`: The previous environment object (whatever the environment passed to\n   the previous parser was as it returned).\n\nYour `chainEnv` function should return whatever should be passed as the\nenvironment object to the next parser in the sequence.\n\nHere's an example, for parsing a sequence of comma-separated consecutive\nintegers:\n\n\u003c!-- !test in chainEnv --\u003e\n\n```js\nconst nextNumberParser = p.from(env =\u003e {\n  const parser = p.map(\n    p.seq([\n      p.string(env.nextNumber.toString()),\n      p.alt([p.string(','), p.eof])\n    ]),\n    ([number, _]) =\u003e number)\n\n  return p.map(\n    parser,\n    (result, env) =\u003e {\n      // Construct new env, rather than mutating the existing one.\n      return { result, nextEnv: { nextNumber: env.nextNumber + 1 } }\n    })\n})\n\nconst manyNumbers = p.times(\n  nextNumberParser, 0, Infinity,\n  // This is the chainEnv argument\n  (numberResult, previousEnv) =\u003e {\n    if (numberResult.nextEnv) return numberResult.nextEnv\n    else return previousEnv\n  })\n\nconst env = { nextNumber: 0 }\nconsole.log(manyNumbers('0,1,2', env))\n```\n\n\u003c!-- !test out chainEnv --\u003e\n\n\u003e ```\n\u003e {\n\u003e   status: true,\n\u003e   index: 5,\n\u003e   value: [\n\u003e     { result: '0', nextEnv: { nextNumber: 1 } },\n\u003e     { result: '1', nextEnv: { nextNumber: 2 } },\n\u003e     { result: '2', nextEnv: { nextNumber: 3 } }\n\u003e   ]\n\u003e }\n\u003e ```\n\n## Limitations\n\n[LL](https://en.wikipedia.org/wiki/LL_parser)(∞) parsers (like this library\ncreates) have these limitations:\n\n - No [left recursion](https://en.wikipedia.org/wiki/Left_recursion).  Grammars\n   that contain left recursion will recurse infinitely and overflow the stack.\n - No [ambiguity](https://en.wikipedia.org/wiki/Ambiguous_grammar).  Ambiguous\n   grammars are allowed and will parse, but will only return the first success\n   or the last failure, not all possible interpretations.\n\n## Related libraries\n\n - [Parsimmon](https://github.com/jneen/parsimmon) is where this library was\n   forked from.  It can recognise the same category of grammars, but can\n   additionally handle binary data.  It has a more abstract API, with a\n   language construction DSL and a call-chaining syntax that some prefer.  It\n   doesn't support user-defined nested environments, and has relatively limited\n   features for modifying parsing logic during parsing.\n - [Nearley](https://github.com/kach/nearley) is much more performant, can\n   parse left-recursive grammars, and even handles ambiguity!  However, it is\n   much more rigid in design: it does not have parse environments, and cannot\n   modify the parser during parsing.\n\n## License\n\n[ISC](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanko%2Fpartser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanko%2Fpartser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanko%2Fpartser/lists"}