├── .gitignore ├── LICENSE ├── README.org ├── dune-project ├── examples ├── calc │ ├── calc.ml │ └── dune └── json │ ├── dune │ └── json.ml └── src ├── combo.ml ├── combo.mli ├── combo.opam ├── dune └── dune-project /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | src/_build/* 3 | _build/* 4 | *.merlin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, yul3n 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * Combo 2 | Combo is a simple parser combinator library for Ocaml providing common parsers 3 | and 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 4 | integrate better with Ocaml and ideas taken from other libraries. It is named 5 | combo because it is based on COMBinig COMBinators in Ocaml. 6 | ** Example 7 | Here is a simple calculator described in the paper: 8 | #+BEGIN_SRC ocaml 9 | open Combo 10 | open Fun 11 | 12 | let positive = int_of_string <$> (inplode <$> many1 digit) 13 | let int = opt id (( * ) (-1) <$ char '-') <*> positive 14 | 15 | let op (f, s) = spaces *> (f <$ word s) <* spaces 16 | let anyop l = choice (List.map op l) 17 | let addops = anyop [(+), "+"; (-), "-"] 18 | let mulops = anyop [( * ), "*"] 19 | let rec expr s = 20 | List.fold_right chainl1 [addops; mulops] (int <|> packs "(" expr ")") s 21 | 22 | let () = 23 | let s = read_line () in 24 | match expr (explode s) with 25 | None -> print_endline "ERROR: bad expression." 26 | | Some (n, _) -> print_int n 27 | #+END_SRC 28 | More examples can be found in the example directory (note: the JSON example 29 | needs this [[https://github.com/ocaml-ppx/ppx_deriving][ppx]]). 30 | ** Installation 31 | Combo is not yet in opam, so it needs ~dune~ to be installed, assuming you are 32 | on a Unix like operating system, you can do: 33 | #+BEGIN_SRC shell 34 | git clone https://github.com/4y8/combo.git 35 | cd combo/src 36 | dune build ./combo.a 37 | dune install 38 | #+END_SRC 39 | ** Documentation (extracted from the mli file) 40 | *** Utils 41 | ~explode s~ turns the string ~s~ into a list of characters. 42 | #+BEGIN_SRC ocaml 43 | val explode : string -> char list 44 | #+END_SRC 45 | ~inplode l~ turns the list of characters ~l~ into a string. 46 | #+BEGIN_SRC ocaml 47 | val inplode : char list -> string 48 | #+END_SRC 49 | ~parser~ is the type of parsers. 50 | #+BEGIN_SRC ocaml 51 | type ('a, 'b) parser = 'a list -> ('b * 'a list) option 52 | #+END_SRC 53 | *** Basic combinators 54 | ~return a~ is a basic combinator that always succeeds returning the value 55 | ~a~. 56 | #+BEGIN_SRC ocaml 57 | val return : 'a -> ('s, 'a) parser 58 | #+END_SRC 59 | ~fail~ is a basic combinator which always fails. 60 | #+BEGIN_SRC ocaml 61 | val fail: ('s, 'a) parser 62 | #+END_SRC 63 | ~p <*> q~ is the sequence combinator appliying the result of parser ~p~ to 64 | the parser ~q~. 65 | #+BEGIN_SRC ocaml 66 | val ( <*> ) : ('s, 'b -> 'a) parser -> ('s, 'b) parser -> ('s, 'a) parser 67 | #+END_SRC 68 | ~p <**> q~ is the sequence combinator applying the result of parser ~q~ to 69 | the parser ~p~, it is the same as ~<*>~ but in the other way. 70 | #+BEGIN_SRC ocaml 71 | val ( <**> ) : ('s, 'b) parser -> ('s, 'b -> 'a) parser -> ('s, 'a) parser 72 | #+END_SRC 73 | ~~ is the reverse sequencing operator but which doesn't modify the first 74 | result if the second one failed. 75 | #+BEGIN_SRC ocaml 76 | val ( ) : ('s, 'a) parser -> ('s, 'a -> 'a) parser -> ('s, 'a) parser 77 | #+END_SRC 78 | Sequence monad. 79 | #+BEGIN_SRC ocaml 80 | val ( >>= ) : ('s, 'a) parser -> ('a -> ('s, 'b) parser) -> ('s, 'b) parser 81 | #+END_SRC 82 | ~p <|> q~ is the choice combinator trying the parser ~p~, if it works, 83 | returns the result, else return the result of the parser ~q~. 84 | #+BEGIN_SRC ocaml 85 | val ( <|> ) : ('s, 'a) parser -> ('s, 'a) parser -> ('s, 'a) parser 86 | #+END_SRC 87 | ~f <$> p~ is the map combinator applying the function ~f~ the witness returned 88 | by the parser ~p~, if he succeeds. 89 | #+BEGIN_SRC ocaml 90 | val ( <$> ) : ('b -> 'a) -> ('s, 'b) parser -> ('s, 'a) parser 91 | #+END_SRC 92 | ~p <&> f~ is the flipped map combinator applying the function ~f~ the witness 93 | returned by the parser ~p~, if he succeeds. 94 | #+BEGIN_SRC ocaml 95 | val ( <&> ) : ('b -> 'a) -> ('s, 'b) parser -> ('s, 'a) parser 96 | #+END_SRC 97 | ~f <$ p~ is the map combinator ignoring the value returned by the parser ~p~. 98 | #+BEGIN_SRC ocaml 99 | val ( <$ ) : 'a -> ('s, 'b) parser -> ('s, 'a) parser 100 | #+END_SRC 101 | ~p $> f~ is the reverse map combinator ignoring the value returned by the parser 102 | ~p~. 103 | #+BEGIN_SRC ocaml 104 | val ( $> ) : ('s, 'a) parser -> 'b -> ('s, 'b) parser 105 | #+END_SRC 106 | ~p *> q~ is the sequence combinator but ignores value returned by the parser 107 | ~p~, it's the missing bracket. 108 | #+BEGIN_SRC ocaml 109 | val ( *> ) : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b) parser 110 | #+END_SRC 111 | ~p <* q~ is the sequence combinator but ignores value returned by the parser 112 | ~q~, it's the missing bracket. 113 | #+BEGIN_SRC ocaml 114 | val ( <* ) : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'a) parser 115 | #+END_SRC 116 | ~p err~ is the error combinator raising the error err if the parser ~p~ 117 | failed. 118 | #+BEGIN_SRC ocaml 119 | val( ) : ('s, 'a) parser -> exn -> ('s, 'a) parser 120 | #+END_SRC 121 | ~choice l~ is a combinator that turns the list of parser ~l~ into a single 122 | one which will match one of them. 123 | #+BEGIN_SRC ocaml 124 | val choice : ('s, 'a) parser list -> ('s, 'a) parser 125 | #+END_SRC 126 | ~seq l~ is a combinator that turns a list of parser ~l~ into a single one 127 | which will match all of them and return the result in a list. 128 | #+BEGIN_SRC ocaml 129 | val seq : ('s, 'a) parser list -> ('s, 'a list) parser 130 | #+END_SRC 131 | ~between open p close~ parses the parser ~open~, then ~p~ and ~close~ and 132 | returns the value of ~p~. 133 | #+BEGIN_SRC ocaml 134 | val between : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'c) parser -> ('s, 'b) parser 135 | #+END_SRC 136 | ~sepBy sep p~ is a parser that parses 0 or more times the parser ~p~ separated 137 | by the parser ~sep~. 138 | #+BEGIN_SRC ocaml 139 | val sepBy : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 140 | #+END_SRC 141 | ~sepBy1 sep p~ is a parser that parses 1 or more times the parser ~p~ separated 142 | by the parser ~sep~. 143 | #+BEGIN_SRC ocaml 144 | val sepBy1 : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 145 | #+END_SRC 146 | ~endBy sep p~ is a parser that parses 0 or more times the parser ~p~ 147 | separated and ended by the parser ~sep~. 148 | #+BEGIN_SRC ocaml 149 | val endBy : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 150 | #+END_SRC 151 | ~endBy1 sep p~ is a parser that parses 1 or more times the parser ~p~ 152 | separated and ended by the parser ~sep~. 153 | #+BEGIN_SRC ocaml 154 | val endBy1 : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 155 | #+END_SRC 156 | ~sepEndBy sep p~ is a parser that parses 0 or more times the parser ~p~ 157 | separated and optionally ended by the parser ~sep~. 158 | #+BEGIN_SRC ocaml 159 | val seEndpBy : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 160 | #+END_SRC 161 | ~sepEndBy1 sep p~ is a parser that parses 1 or more times the parser ~p~ 162 | separated and optionally ended by the parser ~sep~. 163 | #+BEGIN_SRC ocaml 164 | val sepEndBy1 : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 165 | #+END_SRC 166 | *** Lazy Combinators 167 | Lazy combinators are really useful for some recursive combinators that may cause 168 | a stack overflow otherwise. 169 | 170 | ~p <*>| q~ is the lazy sequence combinator appliying the result of parser ~p~ to 171 | the parser ~q~, but only evaluating the parser ~q~ if ~p~ worked. 172 | #+BEGIN_SRC ocaml 173 | val ( <*>| ) : ('s, 'b -> 'a) parser -> ('s, 'b) parser lazy_t -> ('s, 'a) parser 174 | #+END_SRC 175 | ~p <|>| q~ is the lazy choice combinator trying the parser ~p~, if it works, 176 | returns the result, else evaluate the parser ~q~ and returns it result. 177 | #+BEGIN_SRC ocaml 178 | val ( <|>| ) : ('s, 'a) parser -> ('s, 'a) parser lazy_t -> ('s, 'a) parser 179 | #+END_SRC 180 | ~p *>| q~ is the lazy sequence combinator but ignores value returned by the 181 | parser ~p~, it's the missing bracket. The parser ~q~ is evaluated only if ~p~ 182 | succeeded. 183 | #+BEGIN_SRC ocaml 184 | val( *>| ) : ('s, 'a) parser -> ('s, 'b) parser lazy_t -> ('s, 'b) parser 185 | #+END_SRC 186 | ~p <*| q~ is the sequence combinator but ignores value returned by the parser 187 | ~q~, it's the missing bracket. The parser ~q~ is evaluated only if ~p~ 188 | succeeded. 189 | #+BEGIN_SRC ocaml 190 | val( <*| ) : ('s, 'a) parser -> ('s, 'b) parser lazy_t -> ('s, 'a) parser 191 | #+END_SRC 192 | *** Basic parsers 193 | ~satisfyp~ is a parser that matches an element satisfying the predicate ~p~. 194 | #+BEGIN_SRC ocaml 195 | val satisfy: ('a -> bool) -> ('a, 'a) parser 196 | #+END_SRC 197 | ~any~ is a parser that matches anything. 198 | #+BEGIN_SRC ocaml 199 | val any : ('a, 'a) parser 200 | #+END_SRC 201 | ~opt default p~ is parser that runs the parser ~p~ and if it succeeds return 202 | the result, else, it returns the ~default~ value given. 203 | #+BEGIN_SRC ocaml 204 | val opt : 'a -> ('s, 'a) parser -> ('s, 'a) parser 205 | #+END_SRC 206 | ~many p~ is a parser that runs the parser ~p~ 0 or more times and returns 207 | all the obtained results in a list. 208 | #+BEGIN_SRC ocaml 209 | val many : ('s, 'a) parser -> ('s, 'a list) parser 210 | #+END_SRC 211 | ~many1 p~ is a parser that runs the parser ~p~ 1 or more times and returns 212 | all the obtained results in a list. 213 | #+BEGIN_SRC ocaml 214 | val many1 : ('s, 'a) parser -> ('s, 'a list) parser 215 | #+END_SRC 216 | ~chainl1 op p~ is a parser that parses the operand ~p~, as left-associative, 217 | separated by the separator ~op~, one or more times. 218 | #+BEGIN_SRC ocaml 219 | val chainl1 : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> ('s, 'a) parser 220 | #+END_SRC 221 | ~chainl op p default~ is a parser that parses the operand ~p~, as 222 | left-associative, separated by the separator ~op~, if it failed, returns the 223 | value ~default~. 224 | #+BEGIN_SRC ocaml 225 | val chainl : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> 'a -> ('s, 'a) parser 226 | #+END_SRC 227 | ~chainr1 op p~ is a parser that parses the operand ~p~, as right-associative, 228 | separated by the separator ~op~, one or more times. 229 | #+BEGIN_SRC ocaml 230 | val chainr1 : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> ('s, 'a) parser 231 | #+END_SRC 232 | ~chainr op p default~ is a parser that parses the operand ~p~, as 233 | right-associative, separated by the separator ~op~, if it failed, returns the 234 | value ~default~. 235 | #+BEGIN_SRC ocaml 236 | val chainr : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> 'a -> ('s, 'a) parser 237 | #+END_SRC 238 | ~sym s~ is a parser that matches the symbol ~s~. 239 | #+BEGIN_SRC ocaml 240 | val sym : 'a -> ('a, 'a) parser 241 | #+END_SRC 242 | ~syms s~ is a parser that matches the list of symbol ~s~. 243 | #+BEGIN_SRC ocaml 244 | val syms : 'a list -> ('a, 'a list) parser 245 | #+END_SRC 246 | ~char c~ is a parser that matches the character ~c~. 247 | #+BEGIN_SRC ocaml 248 | val char : char -> (char, char) parser 249 | #+END_SRC 250 | ~word w~ is a parser that matches the string ~w~. 251 | #+BEGIN_SRC ocaml 252 | val word : string -> (char, char list) parser 253 | #+END_SRC 254 | ~range l r~ is a parser that matches a character between the characters ~l~ and 255 | ~r~ included. 256 | #+BEGIN_SRC ocaml 257 | val range : char -> char -> (char, char) parser 258 | #+END_SRC 259 | ~lower~ is a parser that matches a lowercase character 260 | #+BEGIN_SRC ocaml 261 | val lower : (char, char) parser 262 | #+END_SRC 263 | ~upper~ is a parser that matches an uppercase character 264 | #+BEGIN_SRC ocaml 265 | val upper : (char, char) parser 266 | #+END_SRC 267 | ~letter~ is a parser that matches an alphabet character. 268 | #+BEGIN_SRC ocaml 269 | val letter : (char, char) parser 270 | #+END_SRC 271 | ~digit~ is a parser that matches a digit. 272 | #+BEGIN_SRC ocaml 273 | val digit : (char, char) parser 274 | #+END_SRC 275 | ~alphaNum~ is a parser that matches a letter or a digit. 276 | #+BEGIN_SRC ocaml 277 | val alphaNum : (char, char) parser 278 | #+END_SRC 279 | ~octDigit~ is a parser that matches an octal digit. 280 | #+BEGIN_SRC ocaml 281 | val octDigit : (char, char) parser 282 | #+END_SRC 283 | ~hexDigit~ is a parser that matches a hexadecimal digit. 284 | #+BEGIN_SRC ocaml 285 | val octDigit : (char, char) parser 286 | #+END_SRC 287 | ~space~ is a parser that matches a space. 288 | #+BEGIN_SRC ocaml 289 | val space : (char, char) parser 290 | #+END_SRC 291 | ~spaces~ is a parser that matches 0 or more spaces. 292 | #+BEGIN_SRC ocaml 293 | val spaces : (char, char list) parser 294 | #+END_SRC 295 | ~newline~ is a parser that matches a newline character. 296 | #+BEGIN_SRC ocaml 297 | val newline : (char, char) parser 298 | #+END_SRC 299 | ~tab~ is a parser that matches a tab character. 300 | #+BEGIN_SRC ocaml 301 | val tab : (char, char) parser 302 | #+END_SRC 303 | ~pack l p r~ is a parser that matches the parser ~p~ between the symbols ~l~ 304 | and ~r~. 305 | #+BEGIN_SRC ocaml 306 | val pack : 's list -> ('s, 'a) parser -> 's list -> ('s, 'a) parser 307 | #+END_SRC 308 | ~packs l p r~ is a parser that matches the parser ~p~ between the strings 309 | ~l~ and ~r~. 310 | #+BEGIN_SRC ocaml 311 | val packs : string -> (char, 'a) parser -> string -> (char, 'a) parser 312 | #+END_SRC 313 | ~oneOf l~ is a parser that matches a symbol from the list ~l~. 314 | #+BEGIN_SRC ocaml 315 | val oneOf : 'a list -> ('a, 'a) parser 316 | #+END_SRC 317 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.7) 2 | -------------------------------------------------------------------------------- /examples/calc/calc.ml: -------------------------------------------------------------------------------- 1 | open Combo 2 | open Fun 3 | 4 | let positive = int_of_string <$> (inplode <$> many1 digit) 5 | let int = opt id (( * ) (-1) <$ char '-') <*> positive 6 | 7 | let op (f, s) = spaces *> (f <$ word s) <* spaces 8 | let anyop l = choice (List.map op l) 9 | let addops = anyop [(+), "+"; (-), "-"] 10 | let mulops = anyop [( * ), "*"] 11 | let rec expr s = 12 | List.fold_right chainl1 [addops; mulops] (int <|> packs "(" expr ")") s 13 | 14 | let () = 15 | let s = read_line () in 16 | match expr (explode s) with 17 | None -> print_endline "ERROR: bad expression." 18 | | Some (n, _) -> print_int n 19 | -------------------------------------------------------------------------------- /examples/calc/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name calc) 3 | (libraries combo)) -------------------------------------------------------------------------------- /examples/json/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name json) 3 | (preprocess (pps ppx_deriving.show ppx_deriving.ord)) 4 | (libraries combo)) -------------------------------------------------------------------------------- /examples/json/json.ml: -------------------------------------------------------------------------------- 1 | open Combo 2 | 3 | type jsonVal 4 | = JNull 5 | | JBool of bool 6 | | JNumber of int 7 | | JString of string 8 | | JArray of jsonVal list 9 | | JObject of (string * jsonVal) list 10 | [@@deriving show] 11 | 12 | let (%) f g x = f (g x) 13 | 14 | let jsonNull = JNull <$ word "null" 15 | 16 | let jsonBool = 17 | let jbool b = JBool b in 18 | (jbool % bool_of_string % inplode) <$> (word "true" <|> word "false") 19 | 20 | let jsonNumber = 21 | let jnumber n = JNumber n in 22 | (jnumber % int_of_string % inplode) <$> (many1 digit) 23 | 24 | let string = 25 | inplode <$> (char '"' *> many (satisfy ((<>) '"')) <* char '"') 26 | 27 | let jsonString = 28 | let jstring s = JString s in 29 | (* Note: no escape sequence in strings. *) 30 | jstring <$> string 31 | 32 | let sepcomma = 33 | spaces *> char ',' <* spaces 34 | 35 | let rec jsonValue s = 36 | choice [jsonNull; jsonBool; jsonNumber; jsonString; jsonArray; jsonObject] s 37 | 38 | and jsonArray s = 39 | let jarray l = JArray l in 40 | (jarray 41 | <$> char '[' 42 | *> spaces 43 | *> sepBy sepcomma jsonValue 44 | <* spaces 45 | <* char ']') s 46 | 47 | and jsonObject s = 48 | let pair = 49 | (fun x y -> x, y) 50 | <$> string 51 | <*> spaces 52 | *> char ':' 53 | *> spaces 54 | *> jsonValue 55 | in 56 | let jobject l = JObject l in 57 | (jobject 58 | <$> char '{' 59 | *> spaces 60 | *> sepBy sepcomma pair 61 | <* spaces 62 | <* char '}') s 63 | 64 | let () = 65 | let s = read_line () in 66 | match jsonValue (explode s) with 67 | None -> print_endline "ERROR: bad expression." 68 | | Some (n, _) -> (print_endline % show_jsonVal) n 69 | -------------------------------------------------------------------------------- /src/combo.ml: -------------------------------------------------------------------------------- 1 | open Fun 2 | 3 | type ('a, 'b) parser = 'a list -> ('b * 'a list) option 4 | 5 | (* Helper functions *) 6 | 7 | let (%) f g x = f (g x) 8 | 9 | let rec explode s = 10 | match s with 11 | "" -> [] 12 | | _ -> 13 | (String.get s 0) :: 14 | (explode (String.sub s 1 (String.length s - 1))) 15 | 16 | let rec inplode s = 17 | match s with 18 | [] -> "" 19 | | hd :: tl -> 20 | (String.make 1 hd) ^ (inplode tl) 21 | 22 | let return a = 23 | fun s -> Some (a, s) 24 | 25 | let fail = 26 | fun _ -> None 27 | 28 | let ( <*> ) p q = 29 | fun s -> 30 | match p s with 31 | None -> None 32 | | Some (a, s) -> 33 | match q s with 34 | None -> None 35 | | Some (b, s) -> Some (a b, s) 36 | 37 | let ( <*>| ) p q = 38 | fun s -> 39 | match p s with 40 | None -> None 41 | | Some (a, s) -> 42 | match (Lazy.force q) s with 43 | None -> None 44 | | Some (b, s) -> Some (a b, s) 45 | 46 | let ( >>= ) p q = 47 | fun s -> 48 | match p s with 49 | None -> None 50 | | Some (a, s) -> 51 | q a s 52 | 53 | let ( <|> ) p q = 54 | fun s -> 55 | match p s with 56 | None -> q s 57 | | Some _ as r -> r 58 | 59 | let ( <|>| ) p q = 60 | fun s -> 61 | match p s with 62 | None -> (Lazy.force q) s 63 | | Some _ as r -> r 64 | 65 | let ( <$> ) f p = 66 | (return f) <*> p 67 | 68 | let ( <&> ) p f = 69 | f <$> p 70 | 71 | let ( <$ ) f p = 72 | (const <$> return f) <*> p 73 | 74 | let ( $> ) p f = 75 | f <$ p 76 | 77 | let ( *> ) p q = 78 | (id <$ p) <*> q 79 | 80 | let ( <* ) p q = 81 | (const <$> p) <*> q 82 | 83 | let ( *>| ) p q = 84 | (id <$ p) <*>| q 85 | 86 | let ( <*| ) p q = 87 | (const <$> p) <*>| q 88 | 89 | let ( <**> ) p q = 90 | (fun x f -> f x) <$> p <*> q 91 | 92 | let ( ) p err = 93 | fun s -> 94 | match p s with 95 | None -> raise err 96 | | x -> x 97 | 98 | let opt default x = 99 | x <|> return default 100 | 101 | let ( ) p q = 102 | p <**> opt id q 103 | 104 | let rec many p = 105 | opt [] (List.cons <$> p <*>| (lazy (many p))) 106 | 107 | let many1 p = 108 | List.cons <$> p <*> many p 109 | 110 | let rec appall x = 111 | function 112 | [] -> x 113 | | hd :: tl -> appall (hd x) tl 114 | 115 | let chainl1 op p = 116 | appall <$> p <*> many (flip <$> op <*> p) 117 | 118 | let chainl op p default = 119 | opt default (chainl1 op p) 120 | 121 | let rec chainr1 op p = 122 | p (flip <$> op <*>| lazy (chainr1 op p)) 123 | 124 | let chainr op p default = 125 | opt default (chainr1 op p) 126 | 127 | let choice l = 128 | List.fold_right (<|>) l fail 129 | 130 | let rec seq = 131 | function 132 | [] -> return [] 133 | | hd :: tl -> List.cons <$> hd <*> seq tl 134 | 135 | let between op p cl = 136 | op *> p <* cl 137 | 138 | let sepBy1 sep p = 139 | (List.cons) <$> p <*> many (sep *> p) 140 | 141 | let sepBy sep p = 142 | opt [] (sepBy1 sep p) 143 | 144 | let endBy1 sep p = 145 | many1 (p <* sep) 146 | 147 | let endBy sep p = 148 | opt [] (endBy1 sep p) 149 | 150 | let sepEndBy1 sep p = 151 | (sepBy1 sep p) <**> (opt id (id <$ sep)) 152 | 153 | let sepEndBy sep p = 154 | opt [] (sepEndBy1 sep p) 155 | 156 | let satisfy p = 157 | fun s -> 158 | match s with 159 | hd :: tl when p hd -> Some (hd, tl) 160 | | _ -> None 161 | 162 | let sym s = 163 | satisfy ((=) s) 164 | 165 | let rec syms s = 166 | match s with 167 | [] -> return [] 168 | | hd :: tl -> 169 | List.cons <$> sym hd <*> syms tl 170 | 171 | let char = 172 | sym 173 | 174 | let word = 175 | syms % explode 176 | 177 | let range l r = 178 | satisfy (fun x -> l <= x && x <= r) 179 | 180 | let lower = 181 | range 'a' 'z' 182 | 183 | let upper = 184 | range 'A' 'Z' 185 | 186 | let letter = 187 | lower <|> upper 188 | 189 | let digit = 190 | range '0' '9' 191 | 192 | let alphaNum = 193 | letter <|> digit 194 | 195 | let octDigit = 196 | range '0' '7' 197 | 198 | let hexDigit = 199 | digit <|> range 'a' 'f' <|> range 'A' 'F' 200 | 201 | let any = 202 | function 203 | [] -> None 204 | | hd :: tl -> Some (hd, tl) 205 | 206 | let space = 207 | char ' ' 208 | 209 | let spaces = 210 | many space 211 | 212 | let tab = 213 | char '\t' 214 | 215 | let newline = 216 | char '\n' 217 | 218 | let pack l p r = 219 | between (syms l) p (syms r) 220 | 221 | let packs l p r = 222 | between (word l) p (word r) 223 | 224 | let oneOf l = 225 | choice (List.map sym l) 226 | -------------------------------------------------------------------------------- /src/combo.mli: -------------------------------------------------------------------------------- 1 | (* Utils ***********************************************************************) 2 | 3 | (** [explode s] turns the string [s] into a list of characters. *) 4 | val explode : string -> char list 5 | 6 | (** [inplode l] turns the list of characters [l] into a string. *) 7 | val inplode : char list -> string 8 | 9 | (** [parser] is the type of parsers. *) 10 | type ('a, 'b) parser = 'a list -> ('b * 'a list) option 11 | 12 | (* Basic combinators ***********************************************************) 13 | 14 | (** [return a] is a basic combinator that always succeeds returning the value 15 | [a]. *) 16 | val return : 'a -> ('s, 'a) parser 17 | 18 | (** [fail] is a basic combinator which always fails. *) 19 | val fail: ('s, 'a) parser 20 | 21 | (** [p <*> q] is the sequence combinator appliying the result of parser [p] to 22 | the parser [q]. *) 23 | val ( <*> ) : ('s, 'b -> 'a) parser -> ('s, 'b) parser -> ('s, 'a) parser 24 | 25 | (** [p <**> q] is the sequence combinator applying the result of parser [q] to 26 | the parser [p], it is the same as [<*>] but in the other way. *) 27 | val ( <**> ) : ('s, 'b) parser -> ('s, 'b -> 'a) parser -> ('s, 'a) parser 28 | 29 | (** [] is the reverse sequencing operator but which doesn't modify the first 30 | result if the second one failed. *) 31 | val ( ) : ('s, 'a) parser -> ('s, 'a -> 'a) parser -> ('s, 'a) parser 32 | 33 | (** Sequence monad. *) 34 | val ( >>= ) : ('s, 'a) parser -> ('a -> ('s, 'b) parser) -> ('s, 'b) parser 35 | 36 | (** [p <|> q] is the choice combinator trying the parser [p], if it works, 37 | returns the result, else return the result of the parser [q]. *) 38 | val ( <|> ) : ('s, 'a) parser -> ('s, 'a) parser -> ('s, 'a) parser 39 | 40 | (** [f <$> p] is the map combinator applying the function [f] the witness 41 | returned by the parser [p], if he succeeds. *) 42 | val ( <$> ) : ('b -> 'a) -> ('s, 'b) parser -> ('s, 'a) parser 43 | 44 | (** [p <&> f] is the flipped map combinator applying the function [f] the 45 | witness returned by the parser [p], if he succeeds. *) 46 | val ( <&> ) : ('s, 'b) parser -> ('b -> 'a) -> ('s, 'a) parser 47 | 48 | (** [f <$ p] is the map combinator ignoring the value returned by the parser 49 | [p]. *) 50 | val ( <$ ) : 'a -> ('s, 'b) parser -> ('s, 'a) parser 51 | 52 | (** [p $> f] is the reverse map combinator ignoring the value returned by the parser 53 | [p]. *) 54 | val ( $> ) : ('s, 'a) parser -> 'b -> ('s, 'b) parser 55 | 56 | (** [p *> q] is the sequence combinator but ignores value returned by the parser 57 | [p], it's the missing bracket. *) 58 | val ( *> ) : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b) parser 59 | 60 | (** [p <* q] is the sequence combinator but ignores value returned by the parser 61 | [q], it's the missing bracket. *) 62 | val ( <* ) : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'a) parser 63 | 64 | (** [p err] is the error combinator raising the error err if the parser [p] 65 | failed. *) 66 | val ( ) : ('s, 'a) parser -> exn -> ('s, 'a) parser 67 | 68 | (** [choice l] is a combinator that turns the list of parser [l] into a single 69 | one which will match one of them. *) 70 | val choice : ('s, 'a) parser list -> ('s, 'a) parser 71 | 72 | (** [seq l] is a combinator that turns a list of parser [l] into a single one 73 | which will match all of them and return the result in a list. *) 74 | val seq : ('s, 'a) parser list -> ('s, 'a list) parser 75 | 76 | (** [between open p close] parses the parser [open], then [p] and [close] and 77 | returns the value of p. *) 78 | val between : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'c) parser -> ('s, 'b) parser 79 | 80 | (** [sepBy sep p] is a parser that parses 0 or more times the parser [p] 81 | separated by the parser [sep]. *) 82 | val sepBy : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 83 | 84 | (** [sepBy1 sep p] is a parser that parses 1 or more times the parser [p] 85 | separated by the parser [sep]. *) 86 | val sepBy1 : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 87 | 88 | (** [endBy sep p] is a parser that parses 0 or more times the parser [p] 89 | separated and ended by the parser [sep]. *) 90 | val endBy : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 91 | 92 | (** [endBy1 sep p] is a parser that parses 1 or more times the parser [p] 93 | separated and ended by the parser [sep]. *) 94 | val endBy1 : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 95 | 96 | (** [sepEndBy sep p] is a parser that parses 0 or more times the parser [p] 97 | separated and optionally ended by the parser [sep]. *) 98 | val sepEndBy : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 99 | 100 | (** [sepEndBy1 sep p] is a parser that parses 1 or more times the parser [p] 101 | separated and optionally ended by the parser [sep]. *) 102 | val sepEndBy1 : ('s, 'a) parser -> ('s, 'b) parser -> ('s, 'b list) parser 103 | 104 | (* Lazy combinators ************************************************************) 105 | 106 | (** [p <*>| q] is the lazy sequence combinator appliying the result of parser 107 | [p] to the parser [q], but only evaluating the parser [q] if [p] worked. *) 108 | val ( <*>| ) : ('s, 'b -> 'a) parser -> ('s, 'b) parser lazy_t -> ('s, 'a) parser 109 | 110 | (** [p <|>| q] is the lazy choice combinator trying the parser [p], if it works, 111 | returns the result, else evaluate the parser [q] and returns it result. *) 112 | val ( <|>| ) : ('s, 'a) parser -> ('s, 'a) parser lazy_t -> ('s, 'a) parser 113 | 114 | (** [p *>| q] is the lazy sequence combinator but ignores value returned by the 115 | parser [p], it's the missing bracket. The parser [q] is evaluated only if [p] 116 | succeeded. *) 117 | val ( *>| ) : ('s, 'a) parser -> ('s, 'b) parser lazy_t -> ('s, 'b) parser 118 | 119 | (** [p <*| q] is the sequence combinator but ignores value returned by the parser 120 | [q], it's the missing bracket. The parser [q] is evaluated only if [p] 121 | succeeded. *) 122 | val ( <*| ) : ('s, 'a) parser -> ('s, 'b) parser lazy_t -> ('s, 'a) parser 123 | 124 | (* Basic parsers ***************************************************************) 125 | 126 | (** [satisfy p is a parser that matches an element satisfying the predicate 127 | [p]. *) 128 | val satisfy : ('a -> bool) -> ('a, 'a) parser 129 | 130 | (** [any] is a parser that matches anything. *) 131 | val any : ('a, 'a) parser 132 | 133 | (** [opt default p] is parser that runs the parser [p] and if it succeeds return 134 | the result, else, it returns the [default] value given. *) 135 | val opt : 'a -> ('s, 'a) parser -> ('s, 'a) parser 136 | 137 | (** [many p] is a parser that runs the parser [p] 0 or more times and returns 138 | all the obtained results in a list. *) 139 | val many : ('s, 'a) parser -> ('s, 'a list) parser 140 | 141 | (** [many1 p] is a parser that runs the parser [p] 1 or more times and returns 142 | all the obtained results in a list. *) 143 | val many1 : ('s, 'a) parser -> ('s, 'a list) parser 144 | 145 | (** [chainl1 op p] is a parser that parses the operand [p], as left-associative, 146 | separated by the separator [op], one or more times. *) 147 | val chainl1 : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> ('s, 'a) parser 148 | 149 | (** [chainl op p default] is a parser that parses the operand [p], as 150 | left-associative, separated by the separator [op], if it failed, returns the 151 | value [default]. *) 152 | val chainl : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> 'a -> ('s, 'a) parser 153 | 154 | (** [chainr1 op p] is a parser that parses the operand [p], as right-associative, 155 | separated by the separator [op], one or more times. *) 156 | val chainr1 : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> ('s, 'a) parser 157 | 158 | (** [chainr op p default] is a parser that parses the operand [p], as 159 | right-associative, separated by the separator [op], if it failed, returns the 160 | value [default]. *) 161 | val chainr : ('s, 'a -> 'a -> 'a) parser -> ('s, 'a) parser -> 'a -> ('s, 'a) parser 162 | 163 | (** [sym s] is a parser that matches the symbol [s]. *) 164 | val sym : 'a -> ('a, 'a) parser 165 | 166 | (** [syms s] is a parser that matches the list of symbol [s]. *) 167 | val syms : 'a list -> ('a, 'a list) parser 168 | 169 | (** [char c] is a parser that matches the character [c]. *) 170 | val char : char -> (char, char) parser 171 | 172 | (** [word w] is a parser that matches the string [w]. *) 173 | val word : string -> (char, char list) parser 174 | 175 | (** [range l r] is a parser that matches a character between the characters [l] 176 | and [r] included. *) 177 | val range : char -> char -> (char, char) parser 178 | 179 | (** [lower] is a parser that matches a lowercase character *) 180 | val lower : (char, char) parser 181 | 182 | (** [upper] is a parser that matches an uppercase character *) 183 | val upper : (char, char) parser 184 | 185 | (** [letter] is a parser that matches an alphabet character. *) 186 | val letter : (char, char) parser 187 | 188 | (** [digit] is a parser that matches a digit. *) 189 | val digit : (char, char) parser 190 | 191 | (** [alphaNum] is a parser that matches a letter or a digit. *) 192 | val alphaNum : (char, char) parser 193 | 194 | (** [octDigit] is a parser that matches an octal digit. *) 195 | val octDigit : (char, char) parser 196 | 197 | (** [hexDigit] is a parser that matches a hexadecimal digit. *) 198 | val hexDigit : (char, char) parser 199 | 200 | (** [space] is a parser that matches a space. *) 201 | val space : (char, char) parser 202 | 203 | (** [spaces] is a parser that matches 0 or more spaces. *) 204 | val spaces : (char, char list) parser 205 | 206 | (** [newline] is a parser that matches a newline character. *) 207 | val newline : (char, char) parser 208 | 209 | (** [tab] is a parser that matches a tab character. *) 210 | val tab : (char, char) parser 211 | 212 | (** [pack l p r] is a parser that matches the parser [p] between the symbols [l] 213 | and [r]. *) 214 | val pack : 's list -> ('s, 'a) parser -> 's list -> ('s, 'a) parser 215 | 216 | (** [packs l p r] is a parser that matches the parser [p] between the strings 217 | [l] and [r]. *) 218 | val packs : string -> (char, 'a) parser -> string -> (char, 'a) parser 219 | 220 | (** [oneOf l] is a parser that matches a symbol from the list [l]. *) 221 | val oneOf : 'a list -> ('a, 'a) parser 222 | 223 | (* Quousque tandem abutere, Catilina, patientia nostra? ************************) 224 | -------------------------------------------------------------------------------- /src/combo.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "combo" 3 | version: "0.1" 4 | maintainer: "yul3n" 5 | authors: "yul3n" 6 | homepage: "https://github.com/Yul3n/combo" 7 | license: "BSD-3" 8 | bug-reports: "https://github.com/Yul3n/combo/issues" 9 | synopsis: "A simple parser combinator library for Ocaml" 10 | description: "Combo is a simple parser combinator library for Ocaml providing 11 | common parsers and combinators." 12 | build: [ 13 | ["dune" "subst"] {pinned} 14 | ["dune" "build" "-p" name "-j" jobs] 15 | ] 16 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name combo) 3 | (public_name combo)) -------------------------------------------------------------------------------- /src/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.7) 2 | --------------------------------------------------------------------------------