Library
Module
Module type
Parameter
Class
Class type
Parser provides functions and types to construct robust, performant and reusable parsers.
At the core is a type t
which represents a constructed parser definition. A parser t
is defined by composing together one or more parsers or t
s via usage of parser operators.
An instance of t
represents an un-evaluated parser. Use parse
function to evaluate it.
input
represents a generalization of data input to parse
. Implement the interface to create new input types.
Parser operators - or functions - are broadly organized into following categories:
An Infix module contains infix and let syntax support functions.
See examples of use.
Represents a parser which can parse value 'a
.
Use parse functions to evaluate a parser.
class type input = object ... end
Represents a generalization of data input source to a parser. Implement this interface to provide new sources of input to parse
.
Include the reparse
package in utop
.
Copy and paste the sample in utop and type ;;
to run it.
#require "reparse";;
Evaluate a parser.
val parse_string : ?track_lnum:bool -> 'a t -> string -> 'a
parse_string ~track_lnum p s
evaluates p
to value v
while consuming string instance s
.
If track_num
is true
then the parser tracks both the line and the column numbers. It is set to false
by default.
Line number and column number both start count from 1
if enabled, 0
otherwise.
Examples
Track line and column number
module P = Reparse.Parser
open P.Infix
;;
let s = "hello world" in
let p = P.(take next *> map2 (fun lnum cnum -> lnum, cnum) lnum cnum) in
let v = P.parse_string ~track_lnum:true p s in
v = (1, 12)
Default behaviour - doesn't track line, column number.
module P = Reparse.Parser
open P.Infix
;;
let s = "hello world" in
let p = P.(take next *> map2 (fun lnum cnum -> lnum, cnum) lnum cnum) in
let v = P.parse_string p s in
v = (0, 0)
parse
is a generalised version of parse_string
over type input
.
Use this function when you have a custom implementation of input
.
Raised by parsers which are unable to parse successfully.
offset
is the current index position of input at the time of failure.
line_number
is line number at the time of failure.
column_number
is column number at the time of failure.
msg
contains an error description.
Create parsers from values.
val pure : 'a -> 'a t
pure v
always parses value v
.
Examples
module P = Reparse.Parser
;;
let input = new P.string_input "" in
let v1 = P.(parse input (pure 5)) in
let v2 = P.(parse input (pure "hello")) in
v1 = 5 && v2 = "hello"
val unit : unit t
unit
is a convenience function to create a new parser which always parses to value ()
.
unit
is pure ()
.
val fail : string -> 'a t
fail err_msg
returns a parser that always fails with err_msg
.
Examples
module P = Reparse.Parser
;;
let input = new P.string_input "" in
let r =
try
let _ = P.(parse input (fail "hello error")) in
assert false
with
| e -> e
in
r = P.Parser { offset = 0; line_number = 0; column_number = 0; msg = "hello error" }
Define parsers by joining two or more parsers.
bind p f
returns a new parser b
where,
a
is the parsed value of p
b
is f a
Examples
module P = Reparse.Parser
;;
let f a = P.pure (a ^ " world") in
let p = P.string "hello" in
let p = P.bind p f in
let input = new P.string_input "hello" in
let b = P.parse input p in
b = "hello world"
See Infix.(>>=)
. p >>= f
is the infix equivalent of bind p f
.
Mappers transform from one parser value to another. map
functions map2, map3, map4
are defined in terms of bind
s. So a given mapper function usage can be defined equivalently in terms of bind
s.
map f p
returns a new parser encapsulating value b
where,
a
is the parsed value of p
.b
is f a
.Examples
module P = Reparse.Parser
;;
let f a = a ^ " world" in
let p = P.string "hello" in
let p = P.map f p in
let b = P.parse p "hello" in
b = "hello world"
Since map
is defined in terms of bind
, the above usage of map
is equivalent to the bind
usage below,
module P = Reparse.Parser
;;
let f a = P.pure (a ^ " world") in
let p = P.string "hello" in
let p = P.bind p f in
let r = P.parse_string p "hello" in
r = "hello world"
See Infix.(<$>)
. f <$> p
is infix equivalent of map f p
.
map2 f p q
returns a new parser encapsulating value c
where,
p
and q
are evaluated sequentially in order as given.a, b
are the parsed values of parsers p
and q
respectively.c
is f a b
.Examples
module P = Reparse.Parser
;;
let f a b = a + b in
let p = P.pure 1 in
let q = P.pure 2 in
let p = P.map2 f p q in
let v = P.parse_string p "" in
v = 3
The above usage of map2
is equivalent to below,
module P = Reparse.Parser
open P.Infix
;;
let p = P.pure 1 >>= fun a -> P.pure 2 >>= fun b -> P.pure (a + b) in
let v = P.parse_string p "" in
v = 3
map3 f p q r
returns a new parser encapsulating value d
where,
p
, q
, r
are evaluated sequentially in order as given.a, b, c
are the parsed values of parsers p
, q
and r
respectively.d
is f a b c
.Examples
module P = Reparse.Parser
;;
let f a b c = a + b + c in
let p = P.pure 1 in
let q = P.pure 2 in
let r = P.pure 3 in
let p = P.map3 f p q r in
let v = P.parse_string p "" in
v = 6
map4 f p q r s
returns a new parser encapsulating value e
where,
p
, q
, r
and s
are evaluated sequentially in order as given.a, b, c, d
are the parsed values of parsers p
, q
, r
and s
respectively.e
is f a b c d
.Examples
module P = Reparse.Parser
;;
let f a b c d = a + b + c + d in
let p = P.pure 1 in
let q = P.pure 2 in
let r = P.pure 3 in
let s = P.pure 4 in
let p = P.map4 f p q r s in
let v = P.parse_string p "" in
v = 10
delay p
returns a parser which lazily parses p
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(delay (lazy (char 'z')) <|> delay (lazy (char 'a'))) in
let v = P.parse_string p "abc" in
v = 'a'
named name p
uses name
as part of an error message when constructing exception Parser
if parse of p
fails.
Also see Infix.((<?>))
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(char 'a' |> named "parse_c") in
let v =
try
let _ = P.parse_string p "zzd" in
assert false
with
| e -> e
in
v
= P.Parser
{ offset = 0
; line_number = 0
; column_number = 0
; msg = "[parse_c] Reparse.Parser.Parser(0, 0, 0, \"[char] expected 'a'\")"
}
One or the other.
any l
parses the value of the first successful parser in list l
.
Specified parsers in l
are evaluated sequentially from left to right. A failed parser doesn't consume any input, i.e. offset
is unaffected.
The parser fails if none of the parsers in l
are evaluated successfully.
Examples
First successful parser result is returned
module P = Reparse.Parser
;;
let p = P.(any [ char 'z'; char 'x'; char 'a' ]) in
let v = P.parse_string p "zabc" in
v = 'z'
;;
let p = P.(any [ char 'z'; char 'x'; char 'a' ]) in
let v = P.parse_string p "xabc" in
v = 'x'
;;
let p = P.(any [ char 'z'; char 'x'; char 'a' ]) in
let v = P.parse_string p "abc" in
v = 'a'
Parser fails when none of the parsers in l
are successful.
let p = P.(any [ char 'z'; char 'x'; char 'a' ]) in
let v =
try
let _ = P.parse_string p "yyy" in
false
with
| _ -> true
in
v = true
Group parsers.
all l
parses all parsers in l
and returns the parsed values.
The parser only succeeds if and only if all of the parsers in l
succeed.
Parsers in l
are evaluated sequentially - from left to right.
Examples
All specified parsers succeed.
module P = Reparse.Parser
;;
let p = P.(all [ char 'a'; char 'b'; char 'c' ]) in
let v = P.parse_string p "abc" in
v = [ 'a'; 'b'; 'c' ]
One of the specified parsers - char 'c'
fails.
module P = Reparse.Parser
;;
let p = P.(all [ char 'a'; char 'b'; char 'c' ]) in
let v =
try
let _ = P.parse_string p "abd" in
false
with
| _ -> true
in
v = true
all_unit l
parses all parsers in l
while discarding the parsed values.
Examples
All specified parsers succeed.
module P = Reparse.Parser
;;
let p = P.(all_unit [ char 'a'; char 'b'; char 'c' ]) in
let v = P.parse_string p "abc" in
v = ()
One of the specified parsers - char 'c'
- fails.
module P = Reparse.Parser
;;
let p = P.(all_unit [ char 'a'; char 'b'; char 'c' ]) in
let v =
try
let _ = P.parse_string p "abd" in
false
with
| _ -> true
in
v = true
recur f
returns a recursive parser. Function value f
accepts a parser p
as its argument and returns a parser q
. Parser q
in its definition can refer to p
and p
can refer to q
in its own definition.
Such parsers are also known as a fixpoint or y combinator.
Discards parsed values.
skip ~at_least ~up_to p
repeatedly parses p
and discards its value.
The lower and upper bound of repetition is specified by arguments at_least
and up_to
respectively. The default value of at_least
is 0. The default value of up_to
is unspecified, i.e. there is no upper limit.
The repetition ends when one of the following occurs:
p
evaluates to failureup_to
upper bound value is reachedThe parser encapsulates the count of times p
was evaluated successfully.
Examples
module P = Reparse.Parser
;;
let p = P.(skip space) in
let v = P.parse_string p " " in
v = 5
skip_while p ~while_
repeatedly parses p
and discards its value if parser while_
parses to value true
.
The repetition ends when one of the following occurs:
p
evaluates to failurewhile_
returns false
Note while_
does not consume input.
The parser encapsulates the count of times p
was evaluated successfully.
Examples
module P = Reparse.Parser
;;
let p = P.(skip_while next ~while_:(is space)) in
let v = P.parse_string p " " in
v = 5
Collects parsed values
take ~at_least ~up_to ~sep_by p
repeatedly parses p
and returns the parsed values.
The lower and upper bound of repetition is specified by arguments at_least
and up_to
respectively. The default value of at_least
is 0
. The default value of up_to
is unspecified, i.e. there is no upper limit.
If sep_by
is specified then the evaluation of p
must be followed by a successful evaluation of sep_by
. The parsed value of sep_by
is discarded.
The repetition ends when one of the following occurs:
p
evaluates to failuresep_by
evaluates to failureup_to
upper boudn value is reachedThe parser fails if the count of repetition of p
does not match the value specified by at_least
.
Examples
Default behaviour.
module P = Reparse.Parser
;;
let p = P.(take (char 'a')) in
let v = P.parse_string p "aaaaa" in
v = [ 'a'; 'a'; 'a'; 'a'; 'a' ]
Specify ~sep_by
.
module P = Reparse.Parser
;;
let p = P.(take ~sep_by:(char ',') (char 'a')) in
let v = P.parse_string p "a,a,a,a,a" in
v = [ 'a'; 'a'; 'a'; 'a'; 'a' ]
Specify lower bound argument at_least
.
module P = Reparse.Parser
;;
let p = P.(take ~at_least:3 ~sep_by:(char ',') (char 'a')) in
let v = P.parse_string p "a,a,a,a,a" in
v = [ 'a'; 'a'; 'a'; 'a'; 'a' ]
Lower bound not met results in error.
module P = Reparse.Parser
;;
let p = P.(take ~at_least:5 ~sep_by:(char ',') (char 'a')) in
let v =
try
let _ = P.parse_string p "a,a,a,a" in
false
with
| _ -> true
in
v = true
Specify upper bound up_to
.
module P = Reparse.Parser
;;
let p = P.(take ~up_to:3 ~sep_by:(char ',') (char 'a')) in
let v = P.parse_string p "a,a,a,a,a" in
v = [ 'a'; 'a'; 'a' ]
take_while ~sep_by p ~while_ p
repeatedly parses p
and returns its value.
p
is evaluated if and only if while_
evaluates to true
.
If sep_by
is specified then the evaluation of p
must be followed by a successful evaluation of sep_by
. The parsed value of sep_by
is discarded.
The repetition ends when one of the following occurs:
p
evaluates to failurewhile_
returns false
sep_by
evaluates to failureNote while_
does not consume input.
Examples
Default behaviour.
module P = Reparse.Parser
;;
let p = P.(take_while ~while_:(is_not (char 'b')) (char 'a')) in
let v = P.parse_string p "aab" in
v = [ 'a'; 'a' ]
Specify sep_by
.
module P = Reparse.Parser
;;
let p = P.(take_while ~sep_by:(char ',') ~while_:(is_not (char 'b')) (char 'a')) in
let v = P.parse_string p "a,a,ab" in
v = [ 'a'; 'a'; 'a' ]
take_between ~sep_by ~start ~end_ p
parses start
and then repeatedly parses p
while the parsed value of p
doesn't equal to parsed value of end_
. After the repetition end, it parses end_
. The parser returns the list of parsed values of p
.
Both start
and end_
parser values are discarded.
If sep_by
is specified then the evaluation of p
must be followed by a successful evaluation of sep_by
. The parsed value of sep_by
is discarded.
The repetition ends when one of the following occurs:
p
evaluates to failureend_
parsed value matches p
parsed valuesep_by
evaluates to failureExamples
module P = Reparse.Parser
;;
let p =
P.(take_between ~sep_by:(char ',') ~start:(P.char '(') ~end_:(char ')') next)
in
let v = P.parse_string p "(a,a,a)" in
v = [ 'a'; 'a'; 'a' ]
take_while_on ~sep_by ~while_ ~on_take p
repeatedly parses p
and calls callback on_take_cb
with the parsed value.
p
is evaluated if and only if while_
evaluates to true
.
If sep_by
is specified then the evaluation of p
must be followed by a successful evaluation of sep_by
. The parsed value of sep_by
is discarded.
p
is evaluated repeatedly. The repetition ends when one of the following occurs:
on_take_cb
is the callback function that is called every time p
is evaluated.
p
evaluates to failurewhile_
returns false
sep_by
evaluates to failuretake_while_cb
is the general version of take_while
. It allows to specify how the value a
is to be collected.
Note while_
does not consume input.
Examples
module P = Reparse.Parser
open P.Infix
;;
let buf = Buffer.create 0 in
let on_take_cb a = Buffer.add_char buf a in
let p = P.(take_while_cb (char 'a') ~while_:(is_not (char 'b')) ~on_take_cb) in
let v = P.parse_string p "aaab" in
let s = Buffer.contents buf in
v = 3 && s = "aaa"
Don't fail when parsing is not successful.
optional p
parses Some a
if successful and None
otherwise. a
is the parsed value of p
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(optional (char 'a')) in
let v = P.parse_string p "ab" in
v = Some 'a'
;;
let p = P.(optional (char 'z')) in
let v = P.parse_string p "ab" in
v = None
val is_eoi : bool t
is_eoi
parses to true
if parser has reached end of input, false
otherwise.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string is_eoi "") in
v = true
;;
let v = P.(parse_string is_eoi "a") in
v = false
val eoi : unit t
eoi
parses end of input. Fails if parser is not at end of input.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string eoi "") in
v = ()
;;
let v =
try
let _ = P.(parse_string eoi "a") in
false
with
| _ -> true
in
v = true
val lnum : int t
lnum
parses the current line number of input. line number count start form 1
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(next *> lnum) in
let v = P.parse_string ~track_lnum:true p "bcb" in
v = 1
val cnum : int t
cnum
parses the current column number. column number count start from 1
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(next *> cnum) in
let v = P.parse_string ~track_lnum:true p "bcb" in
v = 2
val offset : int t
offset
parses the current input offset. offset count start from 0
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(next *> offset) in
let v = P.parse_string ~track_lnum:true p "bcb" in
v = 1
true
, false
, is, is not.
not_ p
parses value ()
if and only if p
fails to parse, otherwise the parse fails.
Examples
module P = Reparse.Parser
;;
let p = P.(not_ (char 'a')) in
let v = P.parse_string p "bbb" in
v = ()
not_followed_by p q
parses value of p
only if immediate and subsequent parse of q
is a failure. Parser q
doesn't consumes any input.
Examples
module P = Reparse.Parser
;;
let p = P.(not_followed_by (char 'a') (char 'a')) in
let v = P.parse_string p "ab" in
v = 'a'
is_not p
parses value true
if p
fails to parse and false
otherwise. Note evaluating p
doesn't consume any input.
Examples
module P = Reparse.Parser
;;
let p = P.(is_not (char 'a')) in
let v = P.parse_string p "bbb" in
v = true
is p
parses true
if p
is successful, false
otherwise. Note evaluation of p
doesn't consume any input.
Examples
module P = Reparse.Parser
;;
let p = P.(is (char 'b')) in
let v = P.parse_string p "bcb" in
v = true
Text parsing.
val peek_char : char t
peek_char t
parses the next character from input without consuming it.
Examples
module P = Reparse.Parser
;;
let p = P.peek_char in
let v = P.parse_string p "hello" in
v = 'h'
Input is not consumed.
module P = Reparse.Parser
;;
let p = P.(peek_char *> offset) in
let v = P.parse_string p "hello" in
v = 0
val peek_string : int -> string t
peek_string n
parse a string of length n
without consuming it.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.peek_string 5 in
let v = P.parse_string p "hello" in
v = "hello"
Input is not consumed.
module P = Reparse.Parser
;;
let p = P.(peek_string 5 *> offset) in
let v = P.parse_string p "hello" in
v = 0
val next : char t
next
parses the next character from input. Fails if input has reached end of input.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string next "hello") in
v = 'h'
val char : char -> char t
char c
parses character c
exactly.
Examples
module P = Reparse.Parser
;;
let p = P.char 'h' in
let v = P.parse_string p "hello" in
v = 'h'
val char_if : (char -> bool) -> char t
char_if f
parses a character c
if f c
is true
.
Examples
module P = Reparse.Parser
;;
let p =
P.char_if (function
| 'a' -> true
| _ -> false)
in
let v = P.parse_string p "abc" in
v = 'a'
val string : ?case_sensitive:bool -> string -> string t
string ~case_sensitive s
parses a string s
exactly.
If case_sensitive
is false
then comparison is done without character case consideration. Default value is true
.
Examples
module P = Reparse.Parser
;;
let p = P.string "hello" in
let v = P.parse_string p "hello world" in
v = "hello"
val string_of_chars : char list -> string t
string_of_chars l
converts char list
l
to string
Examples
module P = Reparse.Parser
;;
let p = P.(take ~sep_by:space next >>= string_of_chars) in
let v = P.parse_string p "h e l l o" in
v = "hello"
val line : [ `LF | `CRLF ] -> string t
line c
parses a line of text from input.
Line delimiter c
can be either `LF
or `CRLF
. This corresponds to \n
or \r\n
character respectively.
Examples
module P = Reparse.Parser
;;
let p = P.line `CRLF in
let v = P.parse_string p "line1\r\nline2" in
v = "line1"
Parsers as defined in RFC 5234, Appendix B.1.
val alpha : char t
alpha
parses a character in range A- Z
or a-z
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(take alpha) in
let v = P.parse_string p "abcdABCD" in
v = [ 'a'; 'b'; 'c'; 'd'; 'A'; 'B'; 'C'; 'D' ]
val alpha_num : char t
alpha_num
parses a character in range A-Z
or a-z
or 0-9
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(take alpha_num) in
let v = P.parse_string p "ab123ABCD" in
v = [ 'a'; 'b'; '1'; '2'; '3'; 'A'; 'B'; 'C'; 'D' ]
val lower_alpha : char t
lower_alpha
parses a character in range a-z
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(take lower_alpha) in
let v = P.parse_string p "abcd" in
v = [ 'a'; 'b'; 'c'; 'd' ]
val upper_alpha : char t
upper_alpha
parses a character in range A-Z
.
Examples
module P = Reparse.Parser
open P.Infix
;;
let p = P.(take upper_alpha) in
let v = P.parse_string p "ABCD" in
v = [ 'A'; 'B'; 'C'; 'D' ]
val bit : char t
bit
parses a character which is either '0'
or '1'
.
Examples
module P = Reparse.Parser
;;
let p = P.(take bit) in
let v = P.parse_string p "0110 ab" in
v = [ '0'; '1'; '1'; '0' ]
val ascii_char : char t
ascii_char
parses any US-ASCII character.
Examples
module P = Reparse.Parser
;;
let p = P.(take ascii_char) in
let v = P.parse_string p "0110 abc '" in
v = [ '0'; '1'; '1'; '0'; ' '; 'a'; 'b'; 'c'; ' '; '\'' ]
val cr : char t
cr
parses character '\r'
.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string cr "\rab") in
v = '\r'
val crlf : string t
crlf
parses string "\r\n"
.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string crlf "\r\n abc") in
v = "\r\n"
val control : char t
control
parses characters in range 0x00 - 0x1F
or character 0x7F
.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string control "\x00") in
v = '\x00'
val digit : char t
digit
parses one of the digit characters, 0 .. 9
.
Examples
module P = Reparse.Parser
;;
let p = P.(take digit) in
let v = P.parse_string p "0123456789a" in
v = [ '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9' ]
val digits : string t
digits
parses one or more digit characters, 0 .. 9
.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string digits "1234 +") in
v = "1234"
val dquote : char t
dquote
parses double quote character '"'
.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string dquote "\"hello ") in
v = '"'
val hex_digit : char t
hex_digit
parses any of the hexadecimal digits - 0..9, A, B, C, D, E, F
.
Examples
module P = Reparse.Parser
;;
let p = P.(take hex_digit) in
let v = P.parse_string p "0ABCDEFa" in
v = [ '0'; 'A'; 'B'; 'C'; 'D'; 'E'; 'F' ]
val htab : char t
htab
parses a horizontal tab character '\t'
.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string htab "\t") in
v = '\t'
val lf : char t
lf
parses a linefeed '\n'
character.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string lf "\n") in
v = '\n'
val octet : char t
octect
parses any character in the range \x00 - \xFF
. Synonym for next
Examples
module P = Reparse.Parser
;;
let p = P.(take octet) in
let v = P.parse_string p "0110 abc '" in
v = [ '0'; '1'; '1'; '0'; ' '; 'a'; 'b'; 'c'; ' '; '\'' ]
val space : char t
space
parses a space character.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string space " abc '") in
v = ' '
val spaces : char list t
spaces
parses one or more spaces.
Examples
module P = Reparse.Parser
;;
let v = P.(parse_string spaces " abc") in
v = [ ' '; ' '; ' ' ]
val vchar : char t
vchar
parses any of the visible - printable - characters.
Examples
module P = Reparse.Parser
;;
let p = P.(take vchar) in
let v = P.parse_string p "0110abc\x00" in
v = [ '0'; '1'; '1'; '0'; 'a'; 'b'; 'c' ]
val whitespace : char t
whitespace
parses a space ' '
or horizontal tab '\t'
character.
Examples
module P = Reparse.Parser
;;
let p = P.(take whitespace) in
let v = P.parse_string p "\t \t " in
v = [ '\t'; ' '; '\t'; ' ' ]
module Infix : sig ... end
Provides functions to support infix and let syntax operators.
An example calculator that supports +,-,*
and /
calculations.
The expression grammar is defined by the following BNF grammar:
<expr> ::= <term> "+" <expr> | <term> <term> ::= <factor> "*" <term> | <factor> <factor> ::= "(" <expr> ")" | integer
module P = Reparse.Parser
open P.Infix
type expr =
| Int of int
| Add of expr * expr
| Sub of expr * expr
| Mult of expr * expr
| Div of expr * expr
let skip_spaces = P.skip P.space
let binop : 'a P.t -> char -> 'b P.t -> ('a -> 'b -> 'c) -> 'c P.t =
fun exp1 op exp2 f ->
P.map3
(fun e1 _ e2 -> f e1 e2)
exp1
(skip_spaces *> P.char op <* skip_spaces)
exp2
;;
let integer : expr P.t =
let+ d = P.digits in
Int (int_of_string d)
;;
let factor : expr P.t -> expr P.t =
fun expr ->
P.any
[ P.char '(' *> skip_spaces *> expr <* skip_spaces <* P.char ')'
; skip_spaces *> integer <* skip_spaces
]
;;
let term : expr P.t -> expr P.t =
fun factor ->
P.recur (fun term ->
let mult = binop factor '*' term (fun e1 e2 -> Mult (e1, e2)) in
let div = binop factor '/' term (fun e1 e2 -> Div (e1, e2)) in
mult <|> div <|> factor)
;;
let expr : expr P.t =
P.recur (fun expr ->
let factor = factor expr in
let term = term factor in
let add = binop term '+' expr (fun e1 e2 -> Add (e1, e2)) in
let sub = binop term '-' expr (fun e1 e2 -> Sub (e1, e2)) in
P.any [ add; sub; term ])
;;
let rec eval : expr -> int = function
| Int i -> i
| Add (e1, e2) -> eval e1 + eval e2
| Sub (e1, e2) -> eval e1 - eval e2
| Mult (e1, e2) -> eval e1 * eval e2
| Div (e1, e2) -> eval e1 / eval e2
;;
(* Test AST *)
let r =
let actual = P.parse_string expr "1*2-4+3" in
let expected = Sub (Mult (Int 1, Int 2), Add (Int 4, Int 3)) in
Bool.equal (expected = actual) true
;;
(* Run the evaluator. *)
let exp_result = eval (P.parse_string expr "12+1*10") |> Int.equal 22
Implements JSON parser as defined in https://tools.ietf.org/html/rfc8259.
Assumes UTF-8 character encoding. However, it doesn't do any validation.
Sample top_level inputs;
parse json_value "true";; parse json_value "false";; parse json_value "null";; parse json_value "123";; parse json_value "123.345";; parse json_value "123e123";; parse json_value "123.33E123";; parse json_value {|{"field1": 123,"field2": "value2"}|};; parse json_value {|{"field1":[123,"hello",-123.23], "field2":123} |};; parse json_value {|{"field1":123, "field2":123} |};; parse json_value {|[123,"hello",-123.23, 123.33e13, 123E23] |};;
module P = Reparse.Parser
open P.Infix
type value =
| Object of (string * value) list
| Array of value list
| Number of
{ negative : bool
; int : string
; frac : string option
; exponent : string option
}
| String of string
| False
| True
| Null
let ws =
P.skip
(P.char_if (function
| ' ' | '\t' | '\n' | '\r' -> true
| _ -> false))
;;
let implode l = List.to_seq l |> String.of_seq
let struct_char c = ws *> P.char c <* ws
let null_value = ws *> P.string "null" *> ws *> P.pure Null
let false_value = ws *> P.string "false" *> ws *> P.pure False
let true_value = ws *> P.string "true" *> ws *> P.pure True
let sprintf = Printf.sprintf
let number_value =
let* negative =
P.optional (P.char '-')
>|= function
| Some '-' -> true
| _ -> false
in
let* int =
let digits1_to_9 =
P.char_if (function
| '1' .. '9' -> true
| _ -> false)
in
let num =
P.map2
(fun first_ch digits -> sprintf "%c%s" first_ch digits)
digits1_to_9
P.digits
in
P.any [ P.string "0"; num ]
in
let* frac = P.optional (P.char '.' *> P.digits) in
let+ exponent =
P.optional
(let* e = P.char 'E' <|> P.char 'e' in
let* sign = P.optional (P.char '-' <|> P.char '+') in
let sign =
match sign with
| Some c -> sprintf "%c" c
| None -> ""
in
let+ digits = P.digits in
sprintf "%c%s%s" e sign digits)
in
Number { negative; int; frac; exponent }
;;
let string =
let escaped =
let ch =
P.char '\\'
*> P.char_if (function
| '"' | '\\' | '/' | 'b' | 'f' | 'n' | 'r' | 't' -> true
| _ -> false)
>|= sprintf "\\%c"
in
let hex4digit =
let+ hex =
P.string "\\u" *> P.take ~at_least:4 ~up_to:4 P.hex_digit >|= implode
in
sprintf "\\u%s" hex
in
P.any [ ch; hex4digit ]
in
let unescaped =
P.take_while
~while_:(P.is_not (P.any [ P.char '\\'; P.control; P.dquote ]))
P.next
>|= implode
in
let+ str = P.dquote *> P.take (P.any [ escaped; unescaped ]) <* P.dquote in
String.concat "" str
;;
let string_value = string >|= fun s -> String s
let json_value =
P.recur (fun value ->
let value_sep = struct_char ',' in
let object_value =
let member =
let* nm = string <* struct_char ':' in
let+ v = value in
nm, v
in
let+ object_value =
struct_char '{' *> P.take member ~sep_by:value_sep <* struct_char '}'
in
Object object_value
in
let array_value =
let+ vals =
struct_char '[' *> P.take value ~sep_by:value_sep <* struct_char ']'
in
Array vals
in
P.any
[ object_value
; array_value
; number_value
; string_value
; false_value
; true_value
; null_value
])
;;
let parse s = P.parse_string json_value s