{"id":13838001,"url":"https://github.com/4y8/combo","last_synced_at":"2025-07-10T19:31:35.530Z","repository":{"id":179873160,"uuid":"305058552","full_name":"4y8/combo","owner":"4y8","description":"A simple parser combinator library for Ocaml","archived":false,"fork":false,"pushed_at":"2021-08-21T06:02:29.000Z","size":54,"stargazers_count":29,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-21T00:32:54.672Z","etag":null,"topics":["monadic-parser-combinators","ocaml-library","parser-combinators"],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/4y8.png","metadata":{"files":{"readme":"README.org","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}},"created_at":"2020-10-18T08:44:09.000Z","updated_at":"2024-11-05T00:23:43.000Z","dependencies_parsed_at":"2024-01-13T17:12:06.024Z","dependency_job_id":null,"html_url":"https://github.com/4y8/combo","commit_stats":null,"previous_names":["4y8/combo"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/4y8/combo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4y8%2Fcombo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4y8%2Fcombo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4y8%2Fcombo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4y8%2Fcombo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4y8","download_url":"https://codeload.github.com/4y8/combo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4y8%2Fcombo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264639850,"owners_count":23642313,"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":["monadic-parser-combinators","ocaml-library","parser-combinators"],"created_at":"2024-08-04T15:01:33.142Z","updated_at":"2025-07-10T19:31:35.287Z","avatar_url":"https://github.com/4y8.png","language":"OCaml","readme":"* Combo\nCombo is a simple parser combinator library for Ocaml providing common parsers\nand combinators. It is highly inspired by [[http://www.cs.uu.nl/research/techreps/repo/CS-2008/2008-044.pdf][this]] paper with a few changes to\nintegrate better with Ocaml and ideas taken from other libraries. It is named\ncombo because it is based on COMBinig COMBinators in Ocaml.\n** Example\nHere is a simple calculator described in the paper:\n#+BEGIN_SRC ocaml\n  open Combo\n  open Fun\n\n  let positive = int_of_string \u003c$\u003e (inplode \u003c$\u003e many1 digit)\n  let int = opt id (( * ) (-1) \u003c$ char '-') \u003c*\u003e positive \n\n  let op (f, s) = spaces *\u003e (f \u003c$ word s) \u003c* spaces\n  let anyop l = choice (List.map op l)\n  let addops = anyop [(+), \"+\"; (-), \"-\"]\n  let mulops = anyop [( * ), \"*\"]\n  let rec expr s =\n    List.fold_right chainl1 [addops; mulops] (int \u003c|\u003e packs \"(\" expr \")\")  s\n\n  let () =\n    let s = read_line () in\n    match expr (explode s) with\n      None -\u003e print_endline \"ERROR: bad expression.\" \n    | Some (n, _) -\u003e print_int n\n#+END_SRC\nMore examples can be found in the example directory (note: the JSON example\nneeds this [[https://github.com/ocaml-ppx/ppx_deriving][ppx]]).\n** Installation\nCombo is not yet in opam, so it needs ~dune~ to be installed, assuming you are\non a Unix like operating system, you can do:\n#+BEGIN_SRC shell\n  git clone https://github.com/4y8/combo.git\n  cd combo/src\n  dune build ./combo.a\n  dune install\n#+END_SRC\n** Documentation (extracted from the mli file)\n*** Utils\n~explode s~ turns the string ~s~ into a list of characters.\n#+BEGIN_SRC ocaml\n  val explode : string -\u003e char list\n#+END_SRC\n~inplode l~ turns the list of characters ~l~ into a string. \n#+BEGIN_SRC ocaml\n  val inplode : char list -\u003e string\n#+END_SRC\n~parser~ is the type of parsers. \n#+BEGIN_SRC ocaml\n  type ('a, 'b) parser = 'a list -\u003e ('b * 'a list) option\n#+END_SRC\n*** Basic combinators\n~return a~ is a basic combinator that always succeeds returning the value\n~a~.\n#+BEGIN_SRC ocaml\n  val return : 'a -\u003e ('s, 'a) parser\n#+END_SRC\n~fail~ is a basic combinator which always fails.\n#+BEGIN_SRC ocaml\n  val fail: ('s, 'a) parser\n#+END_SRC\n~p \u003c*\u003e q~ is the sequence combinator appliying the result of parser ~p~ to\nthe parser ~q~.\n#+BEGIN_SRC ocaml\n  val ( \u003c*\u003e ) : ('s, 'b -\u003e 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~p \u003c**\u003e q~ is the sequence combinator applying the result of parser ~q~ to\nthe parser ~p~, it is the same as ~\u003c*\u003e~ but in the other way.\n#+BEGIN_SRC ocaml\n  val ( \u003c**\u003e ) : ('s, 'b) parser -\u003e ('s, 'b -\u003e 'a) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~\u003c??\u003e~ is the reverse sequencing operator but which doesn't modify the first\nresult if the second one failed.\n#+BEGIN_SRC ocaml\n  val ( \u003c??\u003e ) : ('s, 'a) parser -\u003e ('s, 'a -\u003e 'a) parser -\u003e ('s, 'a) parser\n#+END_SRC\nSequence monad.\n#+BEGIN_SRC ocaml\n  val ( \u003e\u003e= ) : ('s, 'a) parser -\u003e ('a -\u003e ('s, 'b) parser) -\u003e ('s, 'b) parser\n#+END_SRC\n~p \u003c|\u003e q~ is the choice combinator trying the parser ~p~, if it works,\nreturns the result, else return the result of the parser ~q~.\n#+BEGIN_SRC ocaml\n  val ( \u003c|\u003e ) : ('s, 'a) parser -\u003e ('s, 'a) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~f \u003c$\u003e p~ is the map combinator applying the function ~f~ the witness returned\nby the parser ~p~, if he succeeds.\n#+BEGIN_SRC ocaml\n  val ( \u003c$\u003e ) : ('b -\u003e 'a) -\u003e ('s, 'b) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~p \u003c\u0026\u003e f~ is the flipped map combinator applying the function ~f~ the witness\nreturned by the parser ~p~, if he succeeds.\n#+BEGIN_SRC ocaml\n  val ( \u003c\u0026\u003e ) : ('b -\u003e 'a) -\u003e ('s, 'b) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~f \u003c$ p~ is the map combinator ignoring the value returned by the parser ~p~.\n#+BEGIN_SRC ocaml\n  val ( \u003c$ ) : 'a -\u003e ('s, 'b) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~p $\u003e f~ is the reverse map combinator ignoring the value returned by the parser\n~p~.\n#+BEGIN_SRC ocaml\n  val ( $\u003e ) : ('s, 'a) parser -\u003e 'b -\u003e ('s, 'b) parser\n#+END_SRC\n~p *\u003e q~ is the sequence combinator but ignores value returned by the parser\n~p~, it's the missing bracket.\n#+BEGIN_SRC ocaml\n  val ( *\u003e ) : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b) parser\n#+END_SRC\n~p \u003c* q~ is the sequence combinator but ignores value returned by the parser\n~q~, it's the missing bracket.\n#+BEGIN_SRC ocaml\n  val ( \u003c* ) : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~p \u003c?\u003e err~ is the error combinator raising the error err if the parser ~p~\nfailed.\n#+BEGIN_SRC ocaml\n  val( \u003c?\u003e ) : ('s, 'a) parser -\u003e exn -\u003e ('s, 'a) parser\n#+END_SRC\n~choice l~ is a combinator that turns the list of parser ~l~ into a single\none which will match one of them.\n#+BEGIN_SRC ocaml\n  val choice : ('s, 'a) parser list -\u003e ('s, 'a) parser\n#+END_SRC\n~seq l~ is a combinator that turns a list of parser ~l~ into a single one\nwhich will match all of them and return the result in a list.\n#+BEGIN_SRC ocaml\n  val seq : ('s, 'a) parser list -\u003e ('s, 'a list) parser\n#+END_SRC\n~between open p close~ parses the parser ~open~, then ~p~ and ~close~ and\nreturns the value of ~p~.\n#+BEGIN_SRC ocaml\n  val between : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'c) parser -\u003e ('s, 'b) parser\n#+END_SRC\n~sepBy sep p~ is a parser that parses 0 or more times the parser ~p~ separated\nby the parser ~sep~.\n#+BEGIN_SRC ocaml\n  val sepBy : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b list) parser\n#+END_SRC\n~sepBy1 sep p~ is a parser that parses 1 or more times the parser ~p~ separated\nby the parser ~sep~.\n#+BEGIN_SRC ocaml\n  val sepBy1 : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b list) parser\n#+END_SRC\n~endBy sep p~ is a parser that parses 0 or more times the parser ~p~\nseparated and ended by the parser ~sep~.\n#+BEGIN_SRC ocaml\n  val endBy : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b list) parser\n#+END_SRC\n~endBy1 sep p~ is a parser that parses 1 or more times the parser ~p~\nseparated and ended by the parser ~sep~.\n#+BEGIN_SRC ocaml\n  val endBy1 : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b list) parser\n#+END_SRC\n~sepEndBy sep p~ is a parser that parses 0 or more times the parser ~p~\nseparated and optionally ended by the parser ~sep~.\n#+BEGIN_SRC ocaml\n  val seEndpBy : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b list) parser\n#+END_SRC\n~sepEndBy1 sep p~ is a parser that parses 1 or more times the parser ~p~\nseparated and optionally ended by the parser ~sep~.\n#+BEGIN_SRC ocaml\n  val sepEndBy1 : ('s, 'a) parser -\u003e ('s, 'b) parser -\u003e ('s, 'b list) parser\n#+END_SRC\n*** Lazy Combinators\nLazy combinators are really useful for some recursive combinators that may cause\na stack overflow otherwise. \n\n~p \u003c*\u003e| q~ is the lazy sequence combinator appliying the result of parser ~p~ to\nthe parser ~q~, but only evaluating the parser ~q~ if ~p~ worked.\n#+BEGIN_SRC ocaml\n  val ( \u003c*\u003e| ) : ('s, 'b -\u003e 'a) parser -\u003e ('s, 'b) parser lazy_t -\u003e ('s, 'a) parser\n#+END_SRC\n~p \u003c|\u003e| q~ is the lazy choice combinator trying the parser ~p~, if it works,\nreturns the result, else evaluate the parser ~q~ and returns it result.\n#+BEGIN_SRC ocaml\n  val ( \u003c|\u003e| ) : ('s, 'a) parser -\u003e ('s, 'a) parser lazy_t -\u003e ('s, 'a) parser\n#+END_SRC\n~p *\u003e| q~ is the lazy sequence combinator but ignores value returned by the\nparser ~p~, it's the missing bracket. The parser ~q~ is evaluated only if ~p~\nsucceeded.\n#+BEGIN_SRC ocaml\n  val( *\u003e| ) : ('s, 'a) parser -\u003e ('s, 'b) parser lazy_t -\u003e ('s, 'b) parser\n#+END_SRC\n~p \u003c*| q~ is the sequence combinator but ignores value returned by the parser\n~q~, it's the missing bracket. The parser ~q~ is evaluated only if ~p~\nsucceeded.\n#+BEGIN_SRC ocaml\n  val( \u003c*| ) : ('s, 'a) parser -\u003e ('s, 'b) parser lazy_t -\u003e ('s, 'a) parser\n#+END_SRC\n*** Basic parsers\n~satisfyp~ is a parser that matches an element satisfying the predicate ~p~.\n#+BEGIN_SRC ocaml\n  val satisfy: ('a -\u003e bool) -\u003e ('a, 'a) parser\n#+END_SRC\n~any~ is a parser that matches anything.\n#+BEGIN_SRC ocaml\n  val any : ('a, 'a) parser\n#+END_SRC\n~opt default p~ is parser that runs the parser ~p~ and if it succeeds return\nthe result, else, it returns the ~default~ value given.\n#+BEGIN_SRC ocaml\n  val opt : 'a -\u003e ('s, 'a) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~many p~ is a parser that runs the parser ~p~ 0 or more times and returns\nall the obtained results in a list.\n#+BEGIN_SRC ocaml\n  val many : ('s, 'a) parser -\u003e ('s, 'a list) parser\n#+END_SRC\n~many1 p~ is a parser that runs the parser ~p~ 1 or more times and returns\nall the obtained results in a list.\n#+BEGIN_SRC ocaml\n  val many1 : ('s, 'a) parser -\u003e ('s, 'a list) parser\n#+END_SRC\n~chainl1 op p~ is a parser that parses the operand ~p~, as left-associative,\nseparated by the separator ~op~, one or more times.\n#+BEGIN_SRC ocaml\n  val chainl1 : ('s, 'a -\u003e 'a -\u003e 'a) parser -\u003e ('s, 'a) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~chainl op p default~ is a parser that parses the operand ~p~, as\nleft-associative, separated by the separator ~op~, if it failed, returns the\nvalue ~default~.\n#+BEGIN_SRC ocaml\n  val chainl : ('s, 'a -\u003e 'a -\u003e 'a) parser -\u003e ('s, 'a) parser -\u003e 'a -\u003e ('s, 'a) parser\n#+END_SRC\n~chainr1 op p~ is a parser that parses the operand ~p~, as right-associative,\nseparated by the separator ~op~, one or more times.\n#+BEGIN_SRC ocaml\n  val chainr1 : ('s, 'a -\u003e 'a -\u003e 'a) parser -\u003e ('s, 'a) parser -\u003e ('s, 'a) parser\n#+END_SRC\n~chainr op p default~ is a parser that parses the operand ~p~, as\nright-associative, separated by the separator ~op~, if it failed, returns the\nvalue ~default~.\n#+BEGIN_SRC ocaml\n  val chainr : ('s, 'a -\u003e 'a -\u003e 'a) parser -\u003e ('s, 'a) parser -\u003e 'a -\u003e ('s, 'a) parser\n#+END_SRC\n~sym s~ is a parser that matches the symbol ~s~.\n#+BEGIN_SRC ocaml\n  val sym : 'a -\u003e ('a, 'a) parser\n#+END_SRC\n~syms s~ is a parser that matches the list of symbol ~s~.\n#+BEGIN_SRC ocaml\n  val syms : 'a list -\u003e ('a, 'a list) parser\n#+END_SRC\n~char c~ is a parser that matches the character ~c~.\n#+BEGIN_SRC ocaml\n  val char : char -\u003e (char, char) parser\n#+END_SRC\n~word w~ is a parser that matches the string ~w~.\n#+BEGIN_SRC ocaml\n  val word : string -\u003e (char, char list) parser\n#+END_SRC\n~range l r~ is a parser that matches a character between the characters ~l~ and\n~r~ included.\n#+BEGIN_SRC ocaml\n  val range : char -\u003e char -\u003e (char, char) parser\n#+END_SRC\n~lower~ is a parser that matches a lowercase character\n#+BEGIN_SRC ocaml\n  val lower : (char, char) parser\n#+END_SRC\n~upper~ is a parser that matches an uppercase character\n#+BEGIN_SRC ocaml\n  val upper : (char, char) parser\n#+END_SRC\n~letter~ is a parser that matches an alphabet character.\n#+BEGIN_SRC ocaml\n  val letter : (char, char) parser\n#+END_SRC\n~digit~ is a parser that matches a digit.\n#+BEGIN_SRC ocaml\n  val digit : (char, char) parser\n#+END_SRC\n~alphaNum~ is a parser that matches a letter or a digit.\n#+BEGIN_SRC ocaml\n  val alphaNum : (char, char) parser\n#+END_SRC\n~octDigit~ is a parser that matches an octal digit.\n#+BEGIN_SRC ocaml\n  val octDigit : (char, char) parser\n#+END_SRC\n~hexDigit~ is a parser that matches a hexadecimal digit.\n#+BEGIN_SRC ocaml\n  val octDigit : (char, char) parser\n#+END_SRC\n~space~ is a parser that matches a space.\n#+BEGIN_SRC ocaml\n  val space : (char, char) parser\n#+END_SRC\n~spaces~ is a parser that matches 0 or more spaces.\n#+BEGIN_SRC ocaml\n  val spaces : (char, char list) parser\n#+END_SRC\n~newline~ is a parser that matches a newline character.\n#+BEGIN_SRC ocaml\n  val newline : (char, char) parser\n#+END_SRC\n~tab~ is a parser that matches a tab character.\n#+BEGIN_SRC ocaml\n  val tab : (char, char) parser\n#+END_SRC\n~pack l p r~ is a parser that matches the parser ~p~ between the symbols ~l~\nand ~r~.\n#+BEGIN_SRC ocaml\n  val pack : 's list -\u003e ('s, 'a) parser -\u003e 's list -\u003e ('s, 'a) parser\n#+END_SRC\n~packs l p r~ is a parser that matches the parser ~p~ between the strings\n~l~ and ~r~.\n#+BEGIN_SRC ocaml\n  val packs : string -\u003e (char, 'a) parser -\u003e string -\u003e (char, 'a) parser\n#+END_SRC\n~oneOf l~ is a parser that matches a symbol from the list ~l~.\n#+BEGIN_SRC ocaml\n  val oneOf : 'a list -\u003e ('a, 'a) parser\n#+END_SRC\n","funding_links":[],"categories":["OCaml"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4y8%2Fcombo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4y8%2Fcombo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4y8%2Fcombo/lists"}