6.2 FParsec.Primitives
6.2.1 Interface
// FParsec.dll [<AutoOpen>] // module is automatically opened when FParsec namespace is opened module FParsec.Primitives [<Literal>] val Ok: ReplyStatus = ReplyStatus.Ok [<Literal>] val Error: ReplyStatus = ReplyStatus.Error [<Literal>] val FatalError: ReplyStatus = ReplyStatus.FatalError type Parser<'TResult, 'TUserState> = CharStream<'TUserState> -> Reply<'TResult> // Two basic primitives that are only seldomly directly used in user code: val preturn: 'a -> Parser<'a,'u> val pzero: Parser<'a,'u> // Chaining and piping parsers // ============================== val (>>=): Parser<'a,'u> -> ('a -> Parser<'b,'u>) -> Parser<'b,'u> val (>>%): Parser<'a,'u> -> 'b -> Parser<'b,'u> val (>>.): Parser<'a,'u> -> Parser<'b,'u> -> Parser<'b,'u> val (.>>): Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a,'u> val (.>>.): Parser<'a,'u> -> Parser<'b,'u> -> Parser<('a * 'b),'u> val between: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'c,'u> val (|>>): Parser<'a,'u> -> ('a -> 'b) -> Parser<'b,'u> val pipe2: Parser<'a,'u> -> Parser<'b,'u> -> ('a -> 'b -> 'c) -> Parser<'c,'u> val pipe3: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> ('a -> 'b -> 'c -> 'd) -> Parser<'d,'u> val pipe4: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> ('a -> 'b -> 'c -> 'd -> 'e) -> Parser<'e,'u> val pipe5: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> Parser<'e,'u> -> ('a -> 'b -> 'c -> 'd -> 'e -> 'f) -> Parser<'f,'u> // Parsing alternatives and recovering from errors // =============================================== val (<|>): Parser<'a,'u> -> Parser<'a,'u> -> Parser<'a,'u> val choice: seq<Parser<'a,'u>> -> Parser<'a,'u> val choiceL: seq<Parser<'a,'u>> -> string -> Parser<'a,'u> val (<|>%): Parser<'a,'u> -> 'a -> Parser<'a,'u> val opt: Parser<'a,'u> -> Parser<'a option,'u> val optional: Parser<'a,'u> -> Parser<unit,'u> val attempt: Parser<'a,'u> -> Parser<'a,'u> val (>>=?): Parser<'a,'u> -> ('a -> Parser<'b,'u>) -> Parser<'b,'u> val (>>?): Parser<'a,'u> -> Parser<'b,'u> -> Parser<'b,'u> val (.>>?): Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a,'u> val (.>>.?): Parser<'a,'u> -> Parser<'b,'u> -> Parser<('a * 'b),'u> // Conditional parsing and looking ahead // ===================================== val notEmpty: Parser<'a,'u> -> Parser<'a,'u> val followedBy: Parser<'a,'u> -> Parser<unit,'u> val followedByL: Parser<'a,'u> -> string -> Parser<unit,'u> val notFollowedBy: Parser<'a,'u> -> Parser<unit,'u> val notFollowedByL: Parser<'a,'u> -> string -> Parser<unit,'u> val lookAhead: Parser<'a,'u> -> Parser<'a,'u> // Customizing error messages // ========================== val (<?>): Parser<'a,'u> -> string -> Parser<'a,'u> val (<??>): Parser<'a,'u> -> string -> Parser<'a,'u> val fail: string -> Parser<'a,'u> val failFatally: string -> Parser<'a,'u> // Parsing sequences // ================= val tuple2: Parser<'a,'u> -> Parser<'b,'u> -> Parser<('a * 'b),'u> val tuple3: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<('a * 'b * 'c),'u> val tuple4: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> Parser<('a * 'b * 'c * 'd),'u> val tuple5: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> Parser<'e,'u> -> Parser<('a * 'b * 'c * 'd * 'e),'u> val parray: int -> Parser<'a,'u> -> Parser<'a[],'u> val skipArray: int -> Parser<'a,'u> -> Parser<unit,'u> val many: Parser<'a,'u> -> Parser<'a list,'u> val many1: Parser<'a,'u> -> Parser<'a list,'u> val skipMany: Parser<'a,'u> -> Parser<unit,'u> val skipMany1: Parser<'a,'u> -> Parser<unit,'u> val sepBy: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u> val sepBy1: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u> val skipSepBy: Parser<'a,'u> -> Parser<'b,'u> -> Parser<unit,'u> val skipSepBy1: Parser<'a,'u> -> Parser<'b,'u> -> Parser<unit,'u> val sepEndBy: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u> val sepEndBy1: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u> val skipSepEndBy: Parser<'a,'u> -> Parser<'b,'u> -> Parser<unit,'u> val skipSepEndBy1: Parser<'a,'u> -> Parser<'b,'u> -> Parser<unit,'u> val manyTill: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u> val many1Till: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u> val skipManyTill: Parser<'a,'u> -> Parser<'b,'u> -> Parser<unit,'u> val skipMany1Till: Parser<'a,'u> -> Parser<'b,'u> -> Parser<unit,'u> [<CompilationRepresentationFlags.Static>] type Inline = static member inline Many: stateFromFirstElement: ('T -> 'State) * foldState: ('State -> 'T -> 'State) * resultFromState: ('State -> 'Result) * elementParser: Parser<'T,'U> * ?firstElementParser: Parser<'T,'U> * ?resultForEmptySequence: (unit -> 'Result) -> Parser<'Result,'U> static member inline SepBy: stateFromFirstElement: ('T -> 'State) * foldState: ('State -> 'Separator -> 'T -> 'State) * resultFromState: ('State -> 'Result) * elementParser: Parser<'T,'U> * separatorParser: Parser<'Separator,'U> * ?firstElementParser: Parser<'T,'U> * ?resultForEmptySequence: (unit -> 'Result) * ?separatorMayEndSequence: bool -> Parser<'Result,'U> static member inline ManyTill: stateFromFirstElement: ('T -> 'State) * foldState: ('State -> 'T -> 'State) * resultFromStateAndEnd: ('State -> 'E -> 'Result) * elementParser: Parser<'T,'U> * endParser: Parser<'E,'U> * ?firstElementParser: Parser<'T,'U> * ?resultForEmptySequence: ('E -> 'Result) -> Parser<'Result,'U> val chainl1: Parser<'a,'u> -> Parser<('a -> 'a -> 'a),'u> -> Parser<'a,'u> val chainl: Parser<'a,'u> -> Parser<('a -> 'a -> 'a),'u> -> 'a -> Parser<'a,'u> val chainr1: Parser<'a,'u> -> Parser<('a -> 'a -> 'a),'u> -> Parser<'a,'u> val chainr: Parser<'a,'u> -> Parser<('a -> 'a -> 'a),'u> -> 'a -> Parser<'a,'u> // Building parsers using F#'s computation expression syntax type ParserCombinator = // ... val parse: ParserCombinator // Building mutually recursive parser values val createParserForwardedToRef: unit -> Parser<'a,'u> * Parser<'a,'u> ref
6.2.2 Members
[<Literal>] val Ok: ReplyStatus = ReplyStatus.Ok
This ReplyStatus
value indicates that a
parser succeeded.
[<Literal>] val Error: ReplyStatus = ReplyStatus.Error
This ReplyStatus
value indicates that a
parser failed.
[<Literal>] val FatalError: ReplyStatus = ReplyStatus.FatalError
This ReplyStatus
value indicates that a
parser failed and no error recovery (except after backtracking) should be tried.
type Parser<'TResult, 'TUserState> = CharStream<'TUserState> -> Reply<'TResult>
The type of the parser functions supported throughout the FParsec library.
The parser preturn x
always succeeds with the result x
(without changing the parser state).
preturn x
is defined as fun stream -> Reply(x)
.
The parser p >>= f
first applies
the parser p
to the input, then applies the function f
to the result returned by p
and finally applies the parser
returned by f
to the input.
Please see the user’s guide chapter Applying parsers in sequence for an in‐depth discussion of the behaviour of this and other sequencing combinators.
The >>=
combinator is the conceptual foundation for all combinators that
consecutively apply multiple parsers to the input. In order to precisely define its behaviour we give an equivalent definition:
let (>>=) (p: Parser<'a,'u>) (f: 'a -> Parser<'b,'u>) = fun stream -> let reply1 = p stream if reply1.Status = Ok then let p2 = f reply1.Result let stateTag = stream.StateTag let mutable reply2 = p2 stream if stateTag = stream.StateTag then reply2.Error <- mergeErrors reply1.Error reply2.Error reply2 else Reply(reply1.Status, reply1.Error)
The parser p1 >>. p2
applies the
parsers p1
and p2
in sequence and
returns the result of p2
.
p1 >>. p2
is an optimized
implementation of p1 >>= fun _ -> p2
.
val pipe3: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> ('a -> 'b -> 'c -> 'd) -> Parser<'d,'u>
The parser pipe3 p1 p2 p3 f
applies the parsers p1
, p2
and p3
in sequence. It returns the
result of the function application f a b c
, where a
, b
and c
are the results returned by p1
, p2
and p3
.
val pipe4: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> ('a -> 'b -> 'c -> 'd -> 'e) -> Parser<'e,'u>
The parser pipe4 p1 p2 p3 p4 f
applies the parsers p1
, p2
, p3
and p4
in sequence. It returns the result of the function application f a b c d
, where a
, b
, c
and d
are the results returned by p1
, p2
,
p3
and p4
.
val pipe5: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> Parser<'e,'u> -> ('a -> 'b -> 'c -> 'd -> 'e -> 'f) -> Parser<'f,'u>
The parser pipe5 p1 p2 p3 p4 p5 f
applies the parsers p1
, p2
, p3
, p4
and p5
in sequence. It returns the result of the function application f a b c d e
, where a
, b
, c
, d
and
e
are the results returned by p1
,
p2
, p3
, p4
and p5
.
The parser p1 <|> p2
first applies
the parser p1
. If p1
succeeds, the
result of p1
is returned. If p1
fails with a non‐fatal error and without changing the parser state, the parser p2
is applied. Note: The stream position is part of the parser state, so if p1
fails after consuming input, p2
will not be applied.
The choice
combinator is a generalization of <|>
to more than two parsers.
Please see the user’s guide chapter on Parsing alternatives for an in‐depth discussion of the behaviour of this combinator.
The parser attempt p
applies the parser p
. If p
fails after changing the parser
state or with a fatal error, attempt p
will backtrack to the
original parser state and report a non‐fatal error.
The parser p >>=? f
behaves like
p >>= f
, except that it will backtrack to the beginning if the parser returned by f
fails with a non‐fatal error and without changing the parser state, even if p1
has changed the parser state.
The parser p1 >>? p2
behaves like
p1 >>. p2
, except that it will backtrack to the beginning if p2
fails
with a non‐fatal error and without changing the parser state, even if p1
has changed the
parser state.
The parser p1 .>>? p2
behaves like
p1 .>> p2
, except that it will backtrack to the beginning if p2
fails
with a non‐fatal error and without changing the parser state, even if p1
has changed the
parser state.
The parser p1 .>>.? p2
behaves
like p1 .>>. p2
, except that it will backtrack to the beginning if p2
fails
with a non‐fatal error and without changing the parser state, even if p1
has changed the
parser state.
The parser notEmpty p
behaves like p
, except that it fails when p
succeeds without consuming input
or changing the parser state in any other way.
notEmpty
is useful for forcing sequence parsers to consume input. For example, notEmpty (manySatisfy f)
behaves like many1Satisfy f
.
The parser followedBy p
succeeds if the parser p
succeeds at the current position. Otherwise it fails with a non‐fatal error. This parser
never changes the parser state.
If the parser followedBy p
fails, it returns no descriptive
error message. Hence it should only be used together with other parsers that take care of a potential error. Alternatively, followedByL p label
can be used to ensure a more descriptive error message.
The parser followedByL p
behaves like followedBy p
, except that it returns an Expected label
error
message when the parser p
fails.
The parser notFollowedBy p
succeeds if the parser p
fails to parse at the current position. Otherwise it fails with a non‐fatal error. This
parser never changes the parser state.
If the parser notFollowedBy p
fails, it returns no descriptive
error message. Hence it should only be used together with other parsers that take care of a potential error. Alternatively, notFollowedByL p label
can be used to ensure a more descriptive error message.
The parser notFollowedByL p
behaves like notFollowedBy p
, except that it returns an Unexpected label
error
message when the parser p
fails.
The parser lookAhead p
parses p
and restores the original parser state afterwards. If p
fails
after changing the parser state, the error messages are wrapped in a NestedError
. If it succeeds, any error messages are discarded. Fatal errors are turned into normal errors.
The parser p <?> label
applies the
parser p
. If p
does not change the
parser state (usually because p
failed), the error messages are replaced with expected label
.
Please also see the user’s guide chapter on customizing error messages.
The parser p <??> label
behaves
like p <?> label
, except that when p
fails after changing the parser state
(for example, because p
consumes input before it fails), a CompoundError
message is generated with both the given string
label
and the error messages generated by p
.
Please also see the user’s guide chapter on customizing error messages.
The parser fail msg
always fails with a messageError msg
. The string msg
will be displayed together with other error messages generated for the same input
position.
fail msg
is equivalent to fun stream -> Reply(Error, messageError msg)
.
The parser failFatally msg
always fails with a messageError msg
. It
returns with a FatalError
, so that no error recovery
is attempted (except via backtracking constructs).
failFatally msg
is equivalent to fun stream -> Reply(FatalError, messageError msg)
.
The parser tuple3 p1 p2 p3
applies the parsers p1
, p2
and p3
in sequence and returns the results in a tuple.
tuple3 p1 p2 p3
is equivalent to pipe3 p1 p2 p3 (fun a b c -> (a, b, c))
.
val tuple4: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> Parser<('a * 'b * 'c * 'd),'u>
The parser tuple4 p1 p2 p3 p4
applies the parsers p1
, p2
, p3
and p4
in sequence and returns the results in a tuple.
tuple4 p1 p2 p3 p4
is equivalent to pipe4 p1 p2 p3 p4 (fun a b c d -> (a, b, c, d))
.
val tuple5: Parser<'a,'u> -> Parser<'b,'u> -> Parser<'c,'u> -> Parser<'d,'u> -> Parser<'e,'u> -> Parser<('a * 'b * 'c * 'd * 'e),'u>
The parser tuple5 p1 p2 p3 p4 p5
applies the parsers p1
, p2
, p3
,
p4
and p5
in sequence and returns
the results in a tuple.
tuple5 p1 p2 p3 p4 p5
is equivalent to pipe5 p1 p2 p3 p4 p5 (fun a b c d e -> (a, b, c, d, e))
.
The parser parray n p
parses n
occurrences of p
and returns the results
in an array.
For example, parray 3 p
is equivalent to
pipe3 p p p (fun a b c -> [|a;b;c|])
.
The parser many p
repeatedly applies the parser p
until p
fails. It returns a list of the
results returned by p
. At the end of the sequence p
must fail without changing the parser state and without signalling a FatalError
, otherwise many p
will fail with the error reported by p
.
many p
tries to guard against an infinite loop by raising an
exception if p
succeeds without changing the parser state.
The parser sepBy p sep
parses
zero or more occurrences of p
separated by sep
(in EBNF: (p (sep p)*)?
). It returns a list of the results returned by p
.
sepBy p sep
is almost equivalent to
pipe2 p (many (sep >>. p)) (fun hd tl -> hd::tl) <|>% []
, except with regard to a case
rarely encountered in practice: If sep
succeeds without changing the parser state and
p
then fails without changing the state, then sepBy p sep
fails too, while the parser given by the almost
equivalent definition would succeed.
The parser sepBy1 p sep
parses
one or more occurrences of p
separated by sep
(in EBNF: p (sep p)*
).
The parser sepBy1 p
behaves like sepBy p
, except that it requires p
to succeed at least one time. Hence, if sepBy1
succeeds, the
returned list always contains at least one value.
The parser sepEndBy p sep
parses
zero or more occurrences of p
separated and optionally ended by sep
(in EBNF: (p (sep p)* sep?)?
). It returns a list of the results
returned by p
.
sepEndBy p sep
tries to guard against an
infinite loop by raising an exception if p
and sep
succeed without changing the parser state.
The parser sepEndBy1 p sep
parses
one or more occurrences of p
separated and optionally ended by sep
(in EBNF: p (sep p)* sep?
). It returns a list of the results
returned by p
.
The parser sepEndBy1 p
behaves like sepEndBy p
, except that it requires p
to succeed at least one time. Hence, if sepEndBy1
succeeds, the returned list always contains at least one value.
The parser manyTill p endp
repeatedly
applies the parser p
for as long as endp
fails (without changing the parser state). It returns a list of the results returned by p
.
manyTill p endp
is an optimized variant
of many (notFollowedBy endp >>. p) .>> endp
that doesn’t have to apply endp
twice at the end of the sequence and that fails with the error reported by endp
if endp
fails after changing the parser state.
[<CompilationRepresentationFlags.Static>] type Inline =
Inline
is a static class that contains the following inline helper methods for defining
optimized sequence parsers:
static member inline Many: stateFromFirstElement: ('T -> 'State) * foldState: ('State -> 'T -> 'State) * resultFromState: ('State -> 'Result) * elementParser: Parser<'T,'U> * ?firstElementParser: Parser<'T,'U> * ?resultForEmptySequence: (unit -> 'Result) -> Parser<'Result,'U>
Inline.Many
is an inline helper method
for defining optimized sequence parsers.
Inline.Many(stateFromFirstElement, foldState, resultFromState, elementParser)
expands to
an optimized implementation of
many1 elementParser // requires at least 1 element |>> function hd::tl -> resultFromState (List.fold foldState (stateFromFirstElement hd) tl)
The 'State
argument to the foldState
function is completely independent of FParsec’s usual parser state. The term
“accumulator” would be a more accurate name for the argument, but that is just too unwieldy to use in the method signature.
If you pass a value for the optional argument resultForEmptySequence
,
the parser expands to an optimized implementation of
many elementParser // accepts empty sequence |>> function | [] -> resultForEmptySequence() | hd::tl -> resultFromState (List.fold foldState (stateFromFirstElement hd) tl)
If you pass a value for the optional argument firstElementParser
, the
first element of the sequence will be parsed with firstElementParser
instead of elementParser
.
The following example shows how you can use Inline.Many
to define an optimized parser that behaves like many1 p |>> List.reduce f
but avoids the temporary
allocation of a list:
let many1Reduce p f = Inline.Many(elementParser = p, stateFromFirstElement = (fun x0 -> x0), foldState = (fun acc y -> f acc y), resultFromState = (fun acc -> acc))
A simple test run:
> run (many1Reduce (pint32 .>> spaces) (+)) "1 2 3";; val it : ParserResult<int32,unit> = Success: 6
The following example shows how you can use Inline.Many
to create an optimized sequence parser that returns an array instead of a list:
let manyA2 p1 p = Inline.Many(firstElementParser = p1, elementParser = p, stateFromFirstElement = (fun x0 -> let ra = ResizeArray<_>() ra.Add(x0) ra), foldState = (fun ra x -> ra.Add(x); ra), resultFromState = (fun ra -> ra.ToArray()), resultForEmptySequence = (fun () -> [||])) let manyA p = manyA2 p p
static member inline SepBy: stateFromFirstElement: ('T -> 'State) * foldState: ('State -> 'Separator -> 'T -> 'State) * resultFromState: ('State -> 'Result) * elementParser: Parser<'T,'U> * separatorParser: Parser<'Separator,'U> * ?firstElementParser: Parser<'T,'U> * ?resultForEmptySequence: (unit -> 'Result) * ?separatorMayEndSequence: bool -> Parser<'Result,'U>
Inline.SepBy
is an inline helper method
for defining optimized sequence parsers. By default, parsers defined with Inline.SepBy
parse sequences of the form (in EBNF): element (separator element)*
Inline.SepBy(stateFromFirstElement, foldState, resultFromState, elementParser,
separatorParser)
expands to an optimized implementation of
pipe2 elementParser (many (separatorParser .>>. elementParser)) (fun elem0 sepsAndElems -> sepsAndElems |> List.fold (fun acc (sep, e) -> foldState acc sep e) (stateFromFirstElement elem0) |> resultFromState)
For most practical purposes the behaviour of the expanded Inline.SepBy
parser and the above definition based on many
can be considered equivalent, but there is a fringe case where the behaviour
differs: If separatorParser
succeeds without changing the parser state and elementParser
then fails without changing the parser state, then the Inline.SepBy
parser fails too, while the
parser given by the definition based on many
would
succeed.
The 'State
argument to the foldState
function is completely independent of FParsec’s usual parser state. The term
“accumulator” would be a more accurate name for the argument, but that is just too unwieldy to use in the method signature.
If you pass true
as the value for the optional argument separatorMayEndSequence
, a separator may also end the sequence, i.e. the parser will accept sequences of the
following form (in EBNF):
element (separator element)* separator?
Note that foldState
is not called with the value of an ending separator.
If you pass a value for the optional argument resultForEmptySequence
, the parser
returned by Inline.SepBy
will call resultForEmptySequence
to create the parser result when it encounters an empty sequence. If
you don’t pass a resultForEmptySequence
function, the parser will fail for an empty
sequence.
If you pass a value for the optional argument firstElementParser
, the first element of a
sequence will be parsed with firstElementParser
instead of elementParser
.
The following example shows how you can use Inline.SepBy
to define an optimized parser that behaves like sepBy1 p sep |>> List.reduce f
but avoids the temporary allocation of a list:
let sepBy1Reduce p sep f = Inline.SepBy(elementParser = p, separatorParser = sep, stateFromFirstElement = (fun x0 -> x0), foldState = (fun acc _ y -> f acc y), resultFromState = (fun acc -> acc))
A simple test run:
> run (sepBy1Reduce pint32 (pstring "," >>. spaces) (+)) "1, 2, 3";; val it : ParserResult<int32,unit> = Success: 6
The following example shows how one could define CharParsers.stringsSepBy
using Inline.SepBy
:
let stringsSepBy p sep = Inline.SepBy(elementParser = p, separatorParser = sep, stateFromFirstElement = (fun str -> let sb = System.Text.StringBuilder() sb.Append(str : string)), // sb.Append returns sb foldState = (fun sb sep str -> sb.Append(sep : string) .Append(str : string)), resultFromState = (fun sb -> sb.ToString())) let testParser : Parser<string,unit> = stringsSepBy (manySatisfy isLetter) (pstring @"\\" >>% @"\")
static member inline ManyTill: stateFromFirstElement: ('T -> 'State) * foldState: ('State -> 'T -> 'State) * resultFromStateAndEnd: ('State -> 'E -> 'Result) * elementParser: Parser<'T,'U> * endParser: Parser<'E,'U> * ?firstElementParser: Parser<'T,'U> * ?resultForEmptySequence: ('E -> 'Result) -> Parser<'Result,'U>
Inline.ManyTill
is an inline helper
method for defining optimized sequence parsers.
Inline.ManyTill(stateFromFirstElement, foldState, resultFromState, elementParser,
endParser)
expands to an optimized implementation of
many1Till elementParser endParser // requires at least 1 element |>> function hd::tl -> resultFromState (List.fold foldState (stateFromFirstElement hd) tl)
The 'State
argument to the foldState
function is completely independent of FParsec’s usual parser state. The term
“accumulator” would be a more accurate name for the argument, but that is just too unwieldy to use in the method signature.
If you pass a value for the optional argument resultForEmptySequence
,
the parser expands to an optimized implementation of
manyTill elementParser endParser // accepts empty sequence |>> function | [] -> resultForEmptySequence() | hd::tl -> resultFromState (List.fold foldState (stateFromFirstElement hd) tl)
If you pass a value for the optional argument firstElementParser
, the
first element of the sequence will be parsed with firstElementParser
instead of elementParser
.
The following example shows how one could define CharParsers.manyCharsTill2
using Inline.ManyTill
:
let myManyCharsTillApply2 cp1 cp endp f = Inline.ManyTill(firstElementParser = cp1, elementParser = cp, endParser = endp, stateFromFirstElement = (fun c -> let sb = System.Text.StringBuilder() sb.Append(c : char)), // sb.Append returns sb foldState = (fun sb c -> sb.Append(c : char)), resultFromStateAndEnd = (fun sb e -> f (sb.ToString()) e), resultForEmptySequence = (fun e -> f "" e)) let myManyCharsTillApply cp endp f = myManyCharsTillApply2 cp cp endp f let myRestOfLine : Parser<string,unit> = myManyCharsTillApply anyChar ((newline >>% "\\n") <|> (eof >>% "")) (fun str nl -> str + nl)
The parser chainl1 p op
parses one or
more occurrences of p
separated by op
(in EBNF: p (op p)*
). It returns the value obtained by
left associative application of all functions returned by op
to the results
returned by p
, i.e. f_n (...(f_2 (f_1 x_1 x_2) x_3) ...) x_n+1
, where f_1
to f_n
are the functions returned by theparser op
and x_1
to x_n+1
are the values returned by p
. If only a single occurance of
p
and no occurance of op
is parsed,
the result of p
is returned directly.
The chainl1
implementation uses constant stack space.
The parser chainr1 p op
parses one or
more occurrences of p
separated by op
(in EBNF: p (op p)*
). It returns the value obtained by
right associative application of all functions returned by op
to the results
returned by p
, i.e. f1 x_1 (f_2 x_2 (... (f_n x_n x_n+1) ...))
, where f_1
to f_n
are the functions returned by the parser op
and x_1
to x_n+1
are the values returned by p
. If only a single occurance of
p
and no occurance of op
is parsed,
the result of p
is returned directly.
The chainr1
implementation uses constant stack space.
type ParserCombinator =
This class is defined as
[<Sealed>] type ParserCombinator() = member t.Delay(f) = fun state -> (f ()) state member t.Return(x) = preturn x member t.Bind(p, f) = p >>= f member t.Zero() = pzero member t.ReturnFrom(p) = p member t.TryWith(p, cf) = fun state -> try p state with e -> (cf e) state member t.TryFinally(p, ff) = fun state -> try p state finally ff ()
Instances of this class can be used to build parsers using F#’s computation
expression syntax. The default instance for this purpose is parse
.
Please see the user’s guide chapter “Where is the monad?” for an
introduction to the parse {...}
syntax.
Some constructs supported by parse
and their translations
are
let! pat = expr in pexpr ==> expr >>= (fun pat -> pexpr) let pat = expr in pexpr ==> let pat = expr in pexpr do! expr in pexpr ==> expr >>= (fun () -> pexpr) do expr in pexpr ==> expr; pexpr if expr then pexpr1 ==> if expr then pexpr1 else pexpr2 else pexpr2 if expr then pexpr ==> if expr then pexpr1 else pzero return exp ==> preturn rexpr return! expr ==> expr
where expr
is any F# expression and pexpr
is an expression of type Parser<_,_>
. You need to use the !
‐constructs whenever you have a
right hand side expression that evaluates to a parser.
val parse: ParserCombinator
A builder object of type ParserCombinator
for
building parsers using F#’s computation expression syntax.
let p, pRef = createParserForwardedToRef()
creates a parser p
that forwards all calls to the parser in the reference cell pRef
. Initially, pRef
holds a reference to a dummy parser that
raises an exception on any invocation.
The JSON parser example in the tutorial shows how you can use
createParserForwardedToRef
to define a parser for a recursive grammar.