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.