{"id":16219749,"url":"https://github.com/neu-rah/paco","last_synced_at":"2026-02-25T11:42:40.589Z","repository":{"id":70925933,"uuid":"316123658","full_name":"neu-rah/paco","owner":"neu-rah","description":"JavaScript monadic parser combinators","archived":false,"fork":false,"pushed_at":"2025-07-11T18:13:42.000Z","size":205,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-28T22:33:29.242Z","etag":null,"topics":["combinators","functional-js","grammar","meta-parser","monadic","parser"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/neu-rah.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-11-26T04:35:38.000Z","updated_at":"2025-07-11T18:13:45.000Z","dependencies_parsed_at":"2025-09-12T02:35:14.561Z","dependency_job_id":"469ef6f9-35b3-4bfa-95c5-d4f01ecda46f","html_url":"https://github.com/neu-rah/paco","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/neu-rah/paco","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neu-rah%2Fpaco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neu-rah%2Fpaco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neu-rah%2Fpaco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neu-rah%2Fpaco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neu-rah","download_url":"https://codeload.github.com/neu-rah/paco/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neu-rah%2Fpaco/sbom","scorecard":{"id":681278,"data":{"date":"2025-08-11","repo":{"name":"github.com/neu-rah/paco","commit":"78c781d6d53273bbebf32552fb82a2ba74d15aa5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":5,"reason":"6 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 5","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T23:15:05.585Z","repository_id":70925933,"created_at":"2025-08-21T23:15:05.585Z","updated_at":"2025-08-21T23:15:05.585Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29819523,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-25T11:42:28.130Z","status":"ssl_error","status_checked_at":"2026-02-25T11:42:27.690Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["combinators","functional-js","grammar","meta-parser","monadic","parser"],"created_at":"2024-10-10T11:55:38.389Z","updated_at":"2026-02-25T11:42:40.543Z","avatar_url":"https://github.com/neu-rah.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PaCo\n\n**javascript monadic parser combinators**\n-----------------------------------------\n\nThis is a tool for building parsers and parse, so that you do not have to be a parser expert to do it.\n\n```javascript\nconst myParser=\n  skip(char('#'))\n  .then(many(letter).join())\n  .skip(char('-'))\n  .then(digits.join().as(parseInt))\n\nparse(\"\u003e\")(myParser)(\"#AN-123\")\n```\noutputs:\n```javascript\nRight { value: [ 'AN', 123 ] }\n```\n\nAll parsers can chain up or group to form other parsers that still can chain up and group.\n\nSome available metaparsers like `many()`, `some()`, `skip()` can accept other parsers or metaparsers.\n\nSome parsers are already a composition with metaparsers, that is the case of `digits`, it will perform `many(digit)`.\n\n**abbreviations**\n\na single string can now be used in place of a non-chaining parser and it will translate either to a `char` or `string` parser.\n\n\u003e `digits.then('.')` is valid as `digits.then(char('.'))`\n\n## Building objects\n\n`.to(tag)` extender will grab the current parsing group result and store it on object key `tag`\n\nif an object does not exist yet it is created, if there is already an object on the results tail it will be used.\n\n```javascript\nconst kchk=\n  string(\"temp: \")\n  .then(\n    option(\"\",oneOf(\"-+\"))\n    .then(digits)\n    .join().as(parseInt).to(\"temp\")\n    .then(char('K').to(\"unit\"))\n    .verify(o=\u003eo[0].unit==='K'\u0026\u0026o[0].temp\u003e=0)\n    .failMsg(\"positive Kelvin!\")\n  )\n```\n```javascript\n#\u003eres(\"\u003e\")(kchk.parse(\"temp: 12K\")).value \n[ 'temp: ', { temp: 12, unit: 'K' } ]\n```\nor failing:\n```javascript\n#\u003eres(\"\u003e\")(kchk.parse(\"temp: -12K\")).value \n'\u003eerror, expecting positive Kelvin! but found `-` here-\u003e-12K...'\n```\n\nthis, along `.verify`, `.post` and `.as` allow event callbacks and all sort of automation during the parsing, if not then let me know.\n\n**It's now possible to parse this:** _enable/disable by config_\n\nEnable with `config.backtrackExclusions=true`\n\n```javascript\n#\u003econfig.optimize=true//turn on optimizations on construction\n#\u003econfig.backtrackExclusions=true//track exclusions on optimization\n#\u003edigits.join().as(parseInt).then(count(2,digit).join()).parse(\"12345\")\nRight { value: Pair { a: [ 123, '45' ], b: '' } }\n```\n`.then`, `.skip` and others can inject exclusion checks on the chain at construction time.\nWe allow the parser base to be re-writen at construction time, keeping away all checking from parse time.\n\n`many` will peek this injected parameters and possibly exclude them from the sequence match\n\n\u003e one can still call `.optim` even with optimizations turned off  \nhowever backtrack will still respect its flag\n\n\u003eoptimization chain is not very populated yet, there are many things to fit in...\n\n## Config\n\n**module exported variable **\n\n__now on by default (\u003e=1.2)__\n\n```javascript\nvar config={\n  optimize:false,//all optimizations\n  backtrackExclusions: false//exclude next selector root from current loop match\n}\n```\n- **optimize** disable all optimizations when false\n\n- **backtrackExclusions** exclude next parser root from the current selection  \n\n\u003e backtrack can be dismissed for well writen parsers  \n(there is still a ling way to go here)\n\n---\n## .then | .skip\nThe chaining is done with `.then` or `.skip`, the first combines the output, while the second will drop it.\n\n## .else\nProvide an alternative value on parser fail, can modify any parser to have a default and do not fail.\n\n## .optional\nMake the target parser optional, silently fail.\n\n## .or\nParsers can alternate with `.or`\n\n## .seq(separator,terminator,min,max)\nTake the target parser and collect a sequence of it, with optional separator and/or terminator. Also checks min and max (0-\u003eInfinity by default)\n\n\u003e`digit.seq()` =\u003e `many(digit)`\n\n\u003e`digit.seq(null,null,1)` =\u003e `some(digit)`\n\n## .qt(min,max)\nA shortcut to .seq, to parse a specific quantity of target parser\n\n## .notFollowedBy(p)\n\nparser succeeds only if `p` fails\n\n## .lookAhead(p)\n\npredicated `p` with no consume before parsing, if `p` fails the parsing will fail\n\n## .excluding(p)\n\npredicated `p` before parsing, if `p` succeedes the parsing will fail\n\n**must apply to same level parser**. using `.excluding(char(..))` at character level on a string level parser will have no effect\n\n```javascript\ndigits.excluding(oneOf(\"89\"))//this will have no effect\nmany(digit.excluding(oneOf(\"89\")))//but this will\n```\n\n\u003e if optimizatumizing with exclusion back-track, the the first will have effect  \nas PaCo will re-write the base to be exactly the second\n\n## .as\nParse output can be formated with `.as`, it will apply to the parser or group where inserted. `.as` will accept an output transformer function.\n\nOutput transformations can stack up.\n\n## .join\n`.join()` and `.join(«sep»)` are shortcuts for `.as(mappend)` and `.as(o=\u003eo.join(«sep»))`\n\nParsers can group by nesting ex: `x.then( y.then(z).join() )`, here the `join` will only apply to the (y.z) results.\n\nTODO: this (grouping) is not fully generalized yet\n\n## .group\nPut the result inside a list (same as `as(o=\u003e[o])`)\n\n## .verify\n`.verify(func,msg)` function `func` will receive the parse group result (list) and should return `true` if approved or `false` to resume in error with message `msg`.\n\n## .post\n`.post(f)` post-processing the result, this is still a static parser definition. Function `f` return will replace the previous result.\n\n## .failMsg\n`.onFailMsg(msg)` provides a message for a failing parser\n\n## .parse\n`.parse(\"...\")` can be used to quick feed a string to any parser.\nThe result will include both input and output state.\n\n\u003eex: `digits.parse(\"123a\")`\n\n_use `parse` function to get only output_\n\nall transformation definitions should be applyed to the parser and not to the result, so `.parse` should be the last item of the group.\n\na parser can be stored, combined, passed around and perform parsing on many contents many times, all transitory state is kept outside.\n\n### -- failing --\n\nthis parse will fail as it expects at least one digit\n\n```javascript\n#\u003eparse(\"\u003e\")(some(digit))(\"#123\")\nLeft { value: 'error, expecting digit but found `#` here-\u003e#123' }\n```\n## Composition examples\n\n```javascript\n  parse(\"\u003e\")( \n    many(\n      some(digit.or(letter)).join()\n      .skip(spaces)\n    ).join(\"-\")\n  )(\"As armas e os baroes\")\n```\n\nexpected result\n```javascript\nRight { value: [ 'As-armas-e-os-baroes' ] }\n```\n\n```javascript\nconst nr=\n  skip(spaces)\n  .then(digits).join().as(parseInt)//get first digits as number\n  .then(many(//then seek many separated by `,` or '|'\n    skip(spaces)\n    .skip(char(',').or(char('|')))//drop the separators (not included in output)\n    .skip(spaces)\n    .then(digits.join().as(parseInt))\n  )).as(foldr1(a=\u003eb=\u003ea+b))//transform output by adding all values\n\nparse(\"\u003e\")(nr)(\" 12 , 2 | 1\")\n```\n\nexpected result\n```javascript\nRight { value: [ 15 ] }\n```\n\n_above parser could be writen using `sepBy`, we were just emphasizing the combinatory_\n\n## Parsers\n\n- **satisfy(f)** uses a function `char-\u003ebool` to evaluate a character\n\n- **char(c)** matches charater `c`\n\n- **cases(c)** case insensitive character `c` match\n\n- **oneOf(\"...\")** matches any given string character\n\n- **noneOf(\"...\")** matches any character not included in string\n\n- **range(a,z)** matches characters between the given ones (inclusive)\n\n- **digit** any digit `0-9`\n\n- **lower** lower case letters `a-z`\n\n- **upper** upper case letters `A-Z`\n\n- **letter** any letter `a-z` or `A-Z`\n\n- **alphaNum** letter or digit\n\n- **hexDigit** hexadecimal digit\n\n- **octDigit** octal digit\n\n- **space** single space\n\n- **tab** single tab\n\n- **nl** newline\n\n- **cr** carriage return\n\n- **blank** tab or space\n\n- **spaces** optional many space\n\n- **blanks** optional many white space\n\n- ~~**spaces1**~~ one or more spaces -\u003e use `some(space)`\n\n- ~~**blanks1**~~ one or more white spaces -\u003e use `some(blank)`\n\n- **digits** optional many digits\n\n- **eof** end of file\n\n- **string(\"...\")** match with given string\n\n- **cis(\"...\")** non case-sensitive string match\n\n- **regex(expr)** match with regex expression\n\n```javascript\n#\u003eparse(\"\u003e\")(regex(\"#([a-zA-Z]+)[ -]([0-9]+)\"))(\"#an-123...\")\nRight { value: [ 'an', '123' ] }\n```\n\n- **skip(...)** ignore the group/parser output\n\n- **many(p)** optional many ocourences or parser `p` targets. This parser never fails as it can return an empty list.\n\n- **some(p)** one or more ocourences of parser `p` targets\n\n- **manyTill(p,end)** one or more ocourences of parser `p` terminating with parser `end`\n\n- **optional(p)** parse `p` if present, otherwise ignore and continue parsing\n\n- **choice[ps]** parse from a list of alternative parsers, this is just an abbreviation of `.or` sequence.\n\n- **count(n)(p)** parses `n` ocourences of `p`\n\n- **between(open)(close)(p)** parses `p` surounded by `open` and `close`, dropping the delimiters.  \nBe sure to exclude the delimiters from the content or provide any other meaning of content end\n\n```javascript\n#\u003eparse(\"\u003e\")(between(space,space,some(noneOf(\" \"))).join())(\" ab.12 \")\nRight { value: [ 'ab.12' ] }\n```\n\n- **option(x)(p)** parses `p` or returns `x` if it fails, this parser never fails.\n\n```javascript\n#\u003eparse(\"\u003e\")(option([\"0\"])(digit))(\"1\")\nRight { value: [ '1' ] }\n#\u003eparse(\"\u003e\")(option([\"0\"])(digit))(\"\")\nRight { value: [ '0' ] }\n#\u003eparse(\"\u003e\")(option([\"0\"])(digit))(\"#\")\nRight { value: [ '0' ] }\n```\n\n- **optionMaybe(p)** parse `p` and returns `Just` the result or `Nothing` if it fails, this parser never fails\n\n- **sepBy(p)(sep)** parses zero or more ocourences of `p` separated by `sep` and droping the separators, this parser never fails.\n\n- **sepBy1(p)(sep)** parses one or more ocourences of `p` separated by `sep` and droping the separators, this parser never fails.\n\n- **endBy(p)(sep)(end)** parses zero or more ocourences of `p` separated by `sep` droping the separators and terminating with `end`\n\n- **endBy1(p)(sep)(end)** parses one or more ocourences of `p` separated by `sep` droping the separators and terminating with `end`\n\n- **none** non-consume happy parser.\n\n\u003e none is an identity parser, will just output the given input as a successful parse. So it never fails or consumes.  \nWe use it to turn binary combinators into unary metaparsers. That is the case of `.skip(...)`, it uses the `none` parser to be available as a unary modifier `skip()`.  \n`none` can do so for any binary combinator and can apear where you want to disable a part.  \n\n\u003e using `none` as `sep` with `endBy(p,sep,end)` whill silentrly skip the `sep` need.\n\n## try and consume\n\nUntill now, all failing parsers do not consume... lets see... while so, no need to inplement **try*\n\n\u003e to be more accurate, failing parsers do consume, we need the failing point on the reports, however the upper parser might pick the starting point to move on, ignoring the consume (as **try** do).\n\n## Parsers basic IO\n\nFor now parsers accept a state pair of (input,output) and will return `Either`:  \n\n- on error: a pair of an error and the input state.\n- on success: a pair of parsed content and the input state.\n\n_*expect changes on this arguments format (changed on v1.1)_\n\ntesting a simple parser\n\n```javascript\n#\u003edigits.run(Pair([],\"123\"))\nRight { value: Pair { a: [ '1', '2', '3' ], b: '' } }\n```\nThis is the basic form of parsing (feeding a parser). \n\nHowever a `parse` function is available, it will perform as the former but gives only output state or a fancy error message.\n\n```javascript\n#\u003eparse(\"\u003e\")(digits)(\"123\")\nRight { value: [ '1', '2', '3' ] }\n```\nSame with\n\n```javascript\n#\u003edigits.parse(\"123\")\nRight { value: Pair { a: [ '1', '2', '3' ], b: '' } }\n```\nthe only difference is that this last one, as the first will give full output, including the input state.\n\n## utility\n\n### **parse** \n\n`parse(filename)(parser)(input string or stream)`\n\nthe filename is merelly a decoration here, to be used on error report\n\n```javascript\n#\u003eparse(\"\u003e\")(letter.or(digit))(\"1\")\nRight { value: [ '1' ] }\n#\u003eparse(\"\u003e\")(letter.or(digit))(\"a\")\nRight { value: [ 'a' ] }\n#\u003eparse(\"\u003e\")(letter.or(digit))(\"#123\")\nLeft {\n  value: 'error, expecting letter or digit but found `#` here-\u003e#123' }\n```\n\ndirect parse\n```javascript\n#\u003eletter.or(digit).parse(\"1\")\nRight { value: Pair { a: '', b: [ '1' ] } }\n#\u003eletter.or(digit).parse(\"a\")\nRight { value: Pair { a: '', b: [ 'a' ] } }\n#\u003eletter.or(digit).parse(\"#123\")\nLeft { value: Pair { a: '#123', b: 'letter or digit' } }\n```\n\ndesugared parse\n```javascript\n#\u003eletter.or(digit).run(Pair(\"1\",[]))\nRight { value: Pair { a: '', b: [ '1' ] } }\n#\u003eletter.or(digit).run(Pair(\"a\",[]))\nRight { value: Pair { a: '', b: [ 'a' ] } }\n#\u003eletter.or(digit).run(Pair(\"#123\",[]))\nLeft { value: Pair { a: '#123', b: 'letter or digit' } }\n```\n\n### **res(r)** \n\nprocess a parser return to produce a result or error message, discarding input state description.\n\n```javascript\n#\u003eres(\"\u003e\")(letter.then(digits).parse(\"123\"))\nLeft { value: '\u003eerror, expecting letter but found `1` here-\u003e1...' }\n```\nwithout `res()` procesing\n```javascript\n#\u003eletter.then(digits).parse(\"123\")\nLeft { value: Pair { a: '123', b: 'letter' } }\n```\n\n### **.expect**\n\nas a consequence of the error report system we got a parser description for free, no great effort was put to it thou\n\n```javascript\nconst p=\n  optional(skip(char('#')))\n  .then(some(letter).join())\n  .skip(char('-').or(spaces1))\n  .then(digits.join().as(parseInt))\n```\n\ndescription:\n```javascript\n#\u003econsole.log(p.expect)\n```\n```text\noptional skip character `#`\nthen (at least one letter)-\u003ejoin()\nskip character `-` or at least one space\nthen ((digits)-\u003ejoin())-\u003eas(parseInt)\n```\nusing:\n```javascript\n#\u003econsole.log(parse(\"\u003e\")(p)(\"#AN-123\"))\nRight { value: [ 'AN', 123 ] }\n```\n\n## Chronology\n\n### 1.2\n\n**added:**\n- `.seq(...)` super parser\n- `.qt(min[,max])` parser modifyer (quantification), based on .seq()\n- `.group()` as o=\u003e[o]\n- `.else()` provide alternativfe values for failing parsers (they will never fail then)\n- activated optimizations and backtrack analitics\n\n\n### 1.1\n\nUsing character domain analysis to detect parser overlap\n\n```text\n[0-9] ∩ ([0-9] ∪ [a-z]) \n\u003c=\u003e ([0-9] ∩ [0-9]) ∪ ([0-9] ∩ [a-z]) \n\u003c=\u003e ((∅)) ∪ (([0-9])) \n\u003c=\u003e [0-9] ∪ ∅ \n\u003c=\u003e [0-9]\n```\n\nversion 1.1 is a full re-write with focus on speed  \n- output pair content swaped  \n- `many1` replaced by `some`  \n- `onFailMsg` replaced by `failMsg`  \n- Parsers are no longuer functions (they are classes and do not derive from Function anymore) so they must be called with `.run` instead of direct function call.\n\n### \u003c 1.1\n\nsome experiments with composition and parser analysis, coding was easy with no performance care.\n\n_this parser is inspired but not following \"parsec\"_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneu-rah%2Fpaco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneu-rah%2Fpaco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneu-rah%2Fpaco/lists"}