{"id":37016882,"url":"https://github.com/spartanz/parserz","last_synced_at":"2026-01-14T01:55:51.514Z","repository":{"id":57740815,"uuid":"140141210","full_name":"spartanz/parserz","owner":"spartanz","description":"A purely-functional library for creating both parsers, pretty-printers, and grammar definitions from a single, type-safe specification of a grammar","archived":false,"fork":false,"pushed_at":"2020-04-04T16:12:07.000Z","size":318,"stargazers_count":70,"open_issues_count":10,"forks_count":7,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-04-23T05:16:53.562Z","etag":null,"topics":["functional-programming","invertible","parser-combinators","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spartanz.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":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2018-07-08T05:49:34.000Z","updated_at":"2024-03-17T22:56:37.000Z","dependencies_parsed_at":"2022-08-30T21:30:52.541Z","dependency_job_id":null,"html_url":"https://github.com/spartanz/parserz","commit_stats":null,"previous_names":["scalaz/scalaz-parsers"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/spartanz/parserz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spartanz%2Fparserz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spartanz%2Fparserz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spartanz%2Fparserz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spartanz%2Fparserz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spartanz","download_url":"https://codeload.github.com/spartanz/parserz/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spartanz%2Fparserz/sbom","scorecard":{"id":803303,"data":{"date":"2025-08-11","repo":{"name":"github.com/spartanz/parserz","commit":"459cdb9700ae9c806f52eedd31d545aed38c0c6c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"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":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"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":1,"reason":"Found 1/8 approved changesets -- score normalized to 1","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":"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":"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":"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":"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":"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"}},{"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T11:05:41.753Z","repository_id":57740815,"created_at":"2025-08-23T11:05:41.753Z","updated_at":"2025-08-23T11:05:41.753Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408692,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T00:40:43.272Z","status":"ssl_error","status_checked_at":"2026-01-14T00:40:42.636Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["functional-programming","invertible","parser-combinators","scala"],"created_at":"2026-01-14T01:55:50.863Z","updated_at":"2026-01-14T01:55:51.502Z","avatar_url":"https://github.com/spartanz.png","language":"Scala","readme":"Welcome to Parserz\n===\n\n#### zero dependency invertible parser combinators library for Scala\n\n\n[![License](https://img.shields.io/badge/license-Apache%202.0-green)](https://opensource.org/licenses/Apache-2.0)\n[![Build Status](https://travis-ci.org/spartanz/parserz.svg?branch=master)](https://travis-ci.org/spartanz/parserz)\n[![CodeCov](https://codecov.io/gh/spartanz/parserz/branch/master/graph/badge.svg)](https://codecov.io/gh/spartanz/parserz)\n[![Gitter](https://badges.gitter.im/spartanz/community.svg)](https://gitter.im/spartanz/community?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge)\n\n[![Maven Central](https://img.shields.io/maven-central/v/org.spartanz/parserz_2.12.svg?label=maven%20central%202.12)](https://search.maven.org/search?q=g:%22org.spartanz%22%20AND%20a:%22parserz_2.12%22)\n[![Maven Central](https://img.shields.io/maven-central/v/org.spartanz/parserz_2.13.svg?label=maven%20central%202.13)](https://search.maven.org/search?q=g:%22org.spartanz%22%20AND%20a:%22parserz_2.13%22)\n\n---\n\n\n## Summary\n\nParserz is a purely-functional library for creating parsers, pretty-printers, and grammar definitions\n from a single, type-safe specification of a grammar.\n\nThe main idea of the library is to facilitate conversions between types `A` and `B`\n in both directions via single coherent implementation, \n as in contrast of having two separate implementations for `A =\u003e B` and `B =\u003e A`.\n \nThe theory behind this idea is outlined in the paper \n[Invertible Syntax Descriptions](https://www.informatik.uni-marburg.de/~rendel/unparse/rendel10invertible.pdf)\nbut the library does not exactly follow the implementation proposed in paper.\n\n\n\n## Overview and highlights\n\nThe following are the abstractions provided by library\n```scala\ntrait ParsersModule {\n  type Input\n  sealed abstract class Grammar[-SI, +SO, +E, A] {}\n}\n```\n\n`Grammar` is a data type for modelling user-defined grammars and \n`ParsersModule` is a module that contains interpreters of `Grammar` into runnable functions, i.e. parsers and printers, etc.\n\nInternally library follows the \"free encoding\" design where `Grammar` is defined as a generalized algebraic data type (GADT),\nand all interpreters are implemented as traversals of `Grammar`.\n\nTypes:\n - `Input` - the **input** type; values of this type are consumed by parser and produced by printer\n - `A` - the **output** type; values of this type are produced by parser and consumed by printer\n - `E` - the **error** type; values of this type are produced by both parser and printer if they cannot proceed normally\n - `SI` - the **input state** type and\n - `SO` - the **output state** type; values of these types represent state that is threaded through the execution of parser and printer\n\nNote: for the printer or other interpreters, meaning of `Input` and `A` may be completely reversed.\nUnfortunately, Scala does not allow redefining type names based on one's interpretation, \n so we have chosen the parser direction to be the titular one simply because library name is `parserz`.\n\n\n\n## Getting the library\n\nSimply add \n```\nlibraryDependencies += \"org.spartanz\" %% \"parserz\" % \"0.2.0\"\n```\nto the SBT build.\n\n\n\n## Using the library\n\nInstantiate the module\n```scala\nimport org.spartanz.parserz.ParsersModule\n\nobject MyParser extends ParsersModule {\n  override type Input = List[Char]\n}\n```\nwhich also requires defining the input type.\n\nImport combinators\n```scala\nimport MyParser.Grammar._\nimport MyParser._\n```\n\nIt is now possible to create grammars!\n\nLet's create building blocks to parse the following program\n```scala\n\"a(bx(),cy(z()))\"\n```\ninto description on how it can be invoked\n```scala\ncase class Call(name: ::[Char], args: List[Call])\n```\n\nFull working example is [here](https://github.com/spartanz/parserz/blob/master/src/test/scala/org/spartanz/parserz/CallsExampleSpec.scala)\n\n\n### Consuming input\n\n`consume` combinator and its variants are used to take a portion of input\n and return remainder of the input and the consumed value. This is how it's defined:\n\n```scala\nfinal def consume[E, A](to: Input =\u003e E \\/ (Input, A), from: ((Input, A)) =\u003e E \\/ Input): Grammar[Any, Nothing, E, A]\n```\n\nIt requires two functions to be provided both returning either a value or an error, for example\n\n```scala\nval char: G[Char] = consume({\n  case c :: cs =\u003e Right(cs -\u003e c)\n  case Nil     =\u003e Left(\"eoi\")\n}, {\n  case (cs, c) =\u003e Right(c :: cs)\n})\n```\n\nThe `G[A]` is simply a type alias for `Grammar[Any, Nothing, String, A]`, which means\n - state is not used (accepts any state and returns nothing)\n - error type is set to `String`\n - the value taken by this grammar is of type `List[Char]` (as defined above) and produced value is of type `A`\n\n\n### Asserting\n\nThere are combinators that allow to assert the value (and return error if assertion failed),\n for example `filter` and its variants, which is defined as\n ```scala\nfinal def filter[E1 \u003e: E](e: E1)(f: A =\u003e Boolean): Grammar[SI, SO, E1, A]\n```\nIt accepts a predicate function which is applied to the result of existing grammar.\nFor example to test that consumed character is something specific we can do\n\n```scala\nval alpha: G[Char]  = char.filter(\"expected: alphabetical\")(_.isLetter)\nval comma: G[Char]  = char.filter(\"expected: comma\")(_ == ',')\n```\n\nResulting grammars are new bigger building blocks.\n\n\n### Sequencing, alterating and repeating\n\n - `~` or `zip` combines two grammars that produce `A` and `B` into a grammar that produces `Tuple2[A, B]`\n - `|` or `alt` combines two grammars that produce `A` and `B` into a grammar that produces `Either[A, B]`\n - `rep` repeats the grammar zero or more times while it can and \n - `rep1` repeats the grammar one or more times while it can\n\nIn the program `\"a(bx(),cy(z()))\"`, `\"bx(),cy(z())\"` is the list of arguments of function `a`.\nHere is how to parse them: \nit's a call to another function followed by zero or more calls to other functions prefixed by comma, or no calls at all:\n\n```scala\nval args: G[List[Call]] = ((call ~ (comma ~ call).rep) | succeed(Nil)).map({\n  case Left((e1, en)) =\u003e e1 :: en.map(_._2)\n  case Right(_)       =\u003e Nil\n}, {\n  case Nil            =\u003e Right(Nil)\n  case e1 :: en       =\u003e Left((e1, en.map((',', _))))\n})\n```\n\n\n### Recursive grammars\n\nIn the previous example there is a call to `call` grammar that produces a value of `Call`.\nCall is also a recursive data structure, as it references itself:\n```scala\ncase class Call(name: ::[Char], args: List[Call])\n```\n\nTo allow recursion in grammar definitions, current version of library has the `delay` combinator. Here is how \n grammar for `Call` is defined:\n \n ```scala\nlazy val call: G[Call] = delay {\n  (alpha.rep1 ~ paren1 ~ args ~ paren2).map(\n    { case (((name, _), exp), _) =\u003e Call(name, exp) },\n    { case Call(name, exp)       =\u003e (((name, '('), exp), ')') }\n  )\n}\n``` \n\nNotice the `lazy` modifier as well which allows this grammar to be forward-referenced.\n\n\n### Interpreters\n\nAll grammars are now constructed and can be passed to interpreters.\n\n#### Parsing\n\nThe parser is simply a function constructed by the library from the given grammar:\n```scala\nval parser: (Unit, List[Char]) =\u003e (Unit, String \\/ (List[Char], Call)) = MyParser.parser(call)\n```\n\nPassing program `\"a(bx(),cy(z()))\"` into this function (along with state of `Unit`) yields\n```scala\nCall(::('a', Nil), List(\n  Call(::('b', List('x')), Nil), \n  Call(::('c', List('y')), List(\n    Call(::('z', Nil), Nil)\n  ))\n))\n```\nwhich is indeed the structure representing function calls in the program.\n\n\n#### Printing\n\nThe printer is also a function constructed by the library from the given grammar:\n```scala\nval printer: (Unit, (List[Char], Call)) =\u003e (Unit, String \\/ List[Char]) = MyParser.printer(call)\n```\n\nPrinting the value just received from the parser yields the program text back:\n```scala\nprinter((), (Nil, value))._2.map(_.reverse.mkString)\n```\n\nHere `Nil` is initial output, which is, of course, empty,\n and there is a `reverse` done on the output because of the `consume` implementation above which prepends to list.\n\n\n#### Grammar definition\n\nThere is an interpreter that produces a description of the grammar in the Backus–Naur form (BNF).\n\n```scala\nval description: List[String] = MyParser.bnf(call)\n```\n\nFor it to work, grammars can be described with the `tag` combinator (or its symbolic equivalent `@@`):\n\n```scala\nval char: G[Char] = \"char\" @@ consume(...)\nval alpha: G[Char]  = char.filter(\"expected: alphabetical\")(_.isLetter).tag(\"alpha\")\n```\n\nIt interprets directly into a value, which is a printout of all tagged grammars. \nThis is how it looks like for the example above\n\n```\n\u003calpha\u003e       ::= \u003cchar\u003e\n\u003copen paren\u003e  ::= \u003cchar\u003e\n\u003ccomma\u003e       ::= \u003cchar\u003e\n\u003cargs\u003e        ::= (\u003ccall\u003e List(\u003ccomma\u003e \u003ccall\u003e) | )\n\u003cclose paren\u003e ::= \u003cchar\u003e\n\u003ccall\u003e        ::= NEL(\u003calpha\u003e) \u003copen paren\u003e \u003cargs\u003e \u003cclose paren\u003e\n```\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspartanz%2Fparserz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspartanz%2Fparserz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspartanz%2Fparserz/lists"}