{"id":17027369,"url":"https://github.com/mprojectscode/parsinom","last_synced_at":"2026-01-04T05:03:09.201Z","repository":{"id":191486406,"uuid":"684756207","full_name":"mProjectsCode/parsiNOM","owner":"mProjectsCode","description":null,"archived":false,"fork":false,"pushed_at":"2023-11-28T21:34:17.000Z","size":372,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-09T12:11:34.829Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mProjectsCode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-29T19:40:15.000Z","updated_at":"2023-11-03T20:50:23.000Z","dependencies_parsed_at":"2023-09-22T03:08:12.606Z","dependency_job_id":"a54d450d-f76f-4594-9623-e39959ba7e38","html_url":"https://github.com/mProjectsCode/parsiNOM","commit_stats":{"total_commits":64,"total_committers":2,"mean_commits":32.0,"dds":0.046875,"last_synced_commit":"d34013f733cf4019c13b593216fe9fab44ec6f9b"},"previous_names":["mprojectscode/parsinom"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mProjectsCode%2FparsiNOM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mProjectsCode%2FparsiNOM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mProjectsCode%2FparsiNOM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mProjectsCode%2FparsiNOM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mProjectsCode","download_url":"https://codeload.github.com/mProjectsCode/parsiNOM/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244031028,"owners_count":20386534,"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":[],"created_at":"2024-10-14T07:47:43.938Z","updated_at":"2026-01-04T05:03:09.116Z","avatar_url":"https://github.com/mProjectsCode.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nGENERATED FILE - Source File: /mdsource/README.source.md\n--\u003e\n\n# parsiNOM\n\nparsiNOM is a modern, optimized and typesafe parser combinator library, inspired by [parsimmon](https://github.com/jneen/parsimmon).\n\nparsiNOM has not yet reached stable, so breaking changes can still occur in minor versions.\n\n## What is a Parser Combinator?\n\nThe idea behind parser combinator is to construct your parser out of a bunch of small parsers.\nThis makes building parsers easier and more readable.\nOn top of that, parser combinators make testing your parser easier, as every part of the parser, such as the parser for string literals, can be tested individually.\n\n### Important Terms\n\n- `combinator` a function that usually takes in one or more parsers and returns a single combined parser\n- `matcher` a matcher is a parser that is not constructed from other parsers\n- `yield`/`yields` in this case `yield` refers to the value that a parser generates from a specific input string, if it can match. In the code a parser is generic over the value that it yields, meaning `Parser\u003cstring[]\u003e` will yield an array of strings.\n\n### Basic Matchers\n\nparsiNOM provides many matchers, which are documented using doc comments. Here we will look at the two most basic matchers.\n\n#### String Matcher\n\n`P.string(str: string): Parser\u003cstring\u003e`\n\n`P.string` returns a parser that matches `str` and yields `str`.\n\n\u003c!-- snippet: example-string-matcher --\u003e\n\u003ca id='snippet-example-string-matcher'\u003e\u003c/a\u003e\n```ts\nconst parser = P.string('foo'); // matches the string foo\n\nexpect(parser.parse('foo')).toEqual('foo'); // succeeds, yields 'foo'\nexpect(parser.parse('foobar')).toEqual('foo'); // succeeds, yields 'foo'\n\nexpect(() =\u003e parser.parse('')).toThrow(); // fails\nexpect(() =\u003e parser.parse('bar')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L7-L15' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-string-matcher' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nTo assert that the parser is at the end of input after parsing you can use `.thenEof()`.\n\n\u003c!-- snippet: example-string-matcher-then-eof --\u003e\n\u003ca id='snippet-example-string-matcher-then-eof'\u003e\u003c/a\u003e\n```ts\nconst parser = P.string('foo').thenEof(); // matches the string foo\n\nexpect(parser.parse('foo')).toEqual('foo'); // succeeds, yields 'foo'\n\nexpect(() =\u003e parser.parse('')).toThrow(); // fails\nexpect(() =\u003e parser.parse('bar')).toThrow(); // fails\nexpect(() =\u003e parser.parse('foobar')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L19-L27' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-string-matcher-then-eof' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n#### RegExp\n\n`P.regexp(regexp: RegExp, group?: number | undefined): Parser\u003cstring\u003e`\n\n`P.regexp` returns a parser that matches the regexp `regexp` and yields the matched string or optionally a specific capture group.\nMost of the time you want to use `^` to only match at the current parser position.\n\n\u003c!-- snippet: example-regexp-matcher --\u003e\n\u003ca id='snippet-example-regexp-matcher'\u003e\u003c/a\u003e\n```ts\nconst parser = P.regexp(/^[0-9]+/); // matches multiple digits\n\nexpect(parser.parse('1')).toEqual('1'); // succeeds, yields '1'\nexpect(parser.parse('123')).toEqual('123'); // succeeds, yields '123'\nexpect(parser.parse('123foo')).toEqual('123'); // succeeds, yields '123'\n\nexpect(() =\u003e parser.parse('')).toThrow(); // fails\nexpect(() =\u003e parser.parse('foo')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L31-L40' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-regexp-matcher' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n### Basic Combinators\n\nThe most important parser combinators are `or`, `sequence`, `many` and `map`, but parsiNOM provides many other combinators build on top of these.\n\n#### Matching Multiple Options\n\n`P.or\u003cParserArr extends readonly Parser\u003cunknown\u003e[]\u003e(...parsers: ParserArr): Parser\u003cTupleToUnion\u003cDeParserArray\u003cParserArr\u003e\u003e\u003e`\n\n`P.or` accepts any number of parsers as arguments and yields the value of the first parser that succeeds.\nBecause of that the order of the parsers is important.\n\n\u003c!-- snippet: example-or --\u003e\n\u003ca id='snippet-example-or'\u003e\u003c/a\u003e\n```ts\nconst parser = P.or(P.string('a'), P.string('b')).thenEof(); // matches 'a' or 'b'\n\nexpect(parser.parse('a')).toEqual('a'); // succeeds, yields 'a'\nexpect(parser.parse('b')).toEqual('b'); // succeeds, yields 'b'\n\nexpect(() =\u003e parser.parse('')).toThrow(); // fails\nexpect(() =\u003e parser.parse('c')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L44-L52' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-or' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nIn the following example the order of parsers matters.\n\n\u003c!-- snippet: example-or-order-wrong --\u003e\n\u003ca id='snippet-example-or-order-wrong'\u003e\u003c/a\u003e\n```ts\nconst parser = P.or(P.string('a'), P.string('ab')).thenEof(); // matches only 'a'\n\nexpect(parser.parse('a')).toEqual('a'); // succeeds, yields 'a'\n\nexpect(() =\u003e parser.parse('ab')).toThrow(); // fails, since the parser will try to match 'a' first, succeeds and then expects the end of input\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L56-L62' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-or-order-wrong' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\u003c!-- snippet: example-or-order-correct --\u003e\n\u003ca id='snippet-example-or-order-correct'\u003e\u003c/a\u003e\n```ts\nconst parser = P.or(P.string('ab'), P.string('a')).thenEof(); // matches 'ab' or 'a'\n\nexpect(parser.parse('a')).toEqual('a'); // succeeds, yields 'a', the parser will try to match 'ab' first but fails, then it backtracks and tries to match 'a'\nexpect(parser.parse('ab')).toEqual('ab'); // succeeds, yields 'ab'\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L66-L71' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-or-order-correct' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n#### Matching a Sequence\n\n`P.sequence\u003cParserArr extends readonly Parser\u003cunknown\u003e[]\u003e(...parsers: ParserArr): Parser\u003cDeParserArray\u003cParserArr\u003e\u003e`\n\n`P.sequence` accepts any number of parsers as arguments and matches them in order, yielding a tuple pf all of their results.\n\n\u003c!-- snippet: example-sequence --\u003e\n\u003ca id='snippet-example-sequence'\u003e\u003c/a\u003e\n```ts\nconst parser = P.sequence(P.string('a'), P.string('b')).thenEof(); // matches 'a' then 'b'\n\nexpect(parser.parse('ab')).toEqual(['a', 'b']); // succeeds, yields ['a', 'b']\n\nexpect(() =\u003e parser.parse('')).toThrow(); // fails\nexpect(() =\u003e parser.parse('a')).toThrow(); // fails\nexpect(() =\u003e parser.parse('ba')).toThrow(); // fails\nexpect(() =\u003e parser.parse('foo')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L75-L84' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-sequence' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n#### Matching Something Many Times\n\n`Parser.many(): Parser\u003cSType[]\u003e`\n\n`Parser.many` makes a parser match itself as many times as it can in a row, yielding an array of the parser's result.\n\n\u003c!-- snippet: example-many --\u003e\n\u003ca id='snippet-example-many'\u003e\u003c/a\u003e\n```ts\nconst parser = P.string('a').many().thenEof(); // matches 'a' as many times as it can\n\nexpect(parser.parse('')).toEqual([]); // succeeds, yields []\nexpect(parser.parse('a')).toEqual(['a']); // succeeds, yields ['a']\nexpect(parser.parse('aaa')).toEqual(['a', 'a', 'a']); // succeeds, yields ['a', 'a', 'a']\n\nexpect(() =\u003e parser.parse('foo')).toThrow(); // fails\nexpect(() =\u003e parser.parse('aafoo')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L88-L97' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-many' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n#### Transforming what a Parser Yields\n\n`Parser.map\u003cOtherSType extends STypeBase\u003e(fn: (value: SType) =\u003e OtherSType): Parser\u003cOtherSType\u003e`\n\n`Parser.map` allows for the transformation of the yielded value of a parser.\n\n\u003c!-- snippet: example-map --\u003e\n\u003ca id='snippet-example-map'\u003e\u003c/a\u003e\n```ts\nconst parser = P.regexp(/^[0-9]+/).map(x =\u003e Number(x)); // matches a number, yielding the number as a number, not a string\n\nexpect(parser.parse('1')).toEqual(1); // succeeds, yields '1' as a number\nexpect(parser.parse('123')).toEqual(123); // succeeds, yields '123' as a number\n\nexpect(() =\u003e parser.parse('')).toThrow(); // fails\nexpect(() =\u003e parser.parse('foo')).toThrow(); // fails\n```\n\u003csup\u003e\u003ca href='/tests/Examples.test.ts#L101-L109' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-example-map' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n### There are More\n\nparsiNOM has a lot more combinators and matchers, which are documented using doc comments.\n\nparsiNOM also has `P_UTILS` which contains a lot of utility parsers, such as parsers for binary expressions, that can simplify building a parser.\n\n### Examples\n\nThe folder `examples` contains a few parsers written using parsiNOM, which you can use as a reference. The tests for these parsers are located in `tests/examples`.\n\n## Technical Things\n\nparsiNOM parsers are [LL(infinity)](https://en.wikipedia.org/wiki/LL_parser) parser, meaning the following.\n\n- the parser works left to right on the input\n- the parser applies the left most derivation first\n- the parser is a top-down parser\n- the parser is restricted to context-free languages (this might not hold true, since `Parser.chain` exists)\n- the parser does not directly support left recursion (it will lead to an infinite loop), but there is a workaround using `Parser.many` and `Array.reduce`\n- the parser supports infinite lookahead\n\n## Development\n\nparsiNOM uses `tsc` to build and `bun` to test.\n\nDevelopment setup\n\n- install bun (if you don't have it already)\n- run `bun install`\n- run `bun run test` to test\n- run `bun run build` to build parsiNOM\n- run `bun run format` to reformat the code\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmprojectscode%2Fparsinom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmprojectscode%2Fparsinom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmprojectscode%2Fparsinom/lists"}