Combinators: Building Complex Parsers
A "combinator" is a function that takes one or more parsers and returns a new, more powerful parser. They are the glue that allows you to combine the primitive parsers into sophisticated structures that can parse complex grammars.
The XParsec combinators are located in the XParsec.Combinators
module, which is decorated with [<AutoOpen>]
, so they are available automatically in your code.
This page is a reference for the most common and useful combinators.
The parser
Computation Expression
While operators are powerful, the most readable way to sequence multiple parsing steps is often with a parser { ... }
computation expression. It allows you to write parsers in a familiar, sequential style.
open XParsec.CharParsers
let pJsonString = between (pchar '"') (pchar '"') (manyChars (pitem '\\' >>. pid <|> noneOf ['"']))
let pJsonNumber = pfloat |>> string
let pKeyValuePair = parser {
let! key = pJsonString
do! pchar ':'
let! value = pJsonString <|> pJsonNumber
return (key, value)
}
Sequencing Combinators
These combinators and operators run parsers one after another, combining their results or discarding them as needed.
For the examples in this section, let's assume we have these simple parsers defined:
let pA = pitem 'a'
let pB = pitem 'b'
let pNum = pint32
Operator / Function |
Description |
Example Usage & Result |
---|---|---|
|
Runs |
|
|
Runs |
|
|
Runs |
|
|
Runs both |
|
|
Runs |
|
|
Runs |
|
|
A convenient way to run |
|
Choice and Option Combinators
These combinators allow you to try different parsing paths or handle optional parts of a grammar.
For these examples, we'll use:
let pA = pitem 'a'
let pB = pitem 'b'
let pFail = fail "this always fails"
Operator / Function |
Description |
Example Usage & Result |
---|---|---|
|
Tries |
|
|
Tries a sequence of parsers |
|
|
Tries parser |
|
|
Tries parser |
|
|
Tries |
|
Repetition Combinators
These combinators apply a parser zero or more times to handle repeating patterns.
For these examples, we'll use:
let pA = pitem 'a'
let pComma = pitem ','
let pPlus = pchar '+' >>% (+) // Parses '+' and returns the addition function
Function |
Description |
Example Usage & Result |
---|---|---|
|
Applies |
|
|
Applies |
|
|
Like |
|
|
Like |
|
|
Parses zero or more occurrences of |
|
|
Parses one or more occurrences of |
|
|
Applies |
|
|
Parses one or more |
|
|
Like |
|
Lookahead and Assertion Combinators
These combinators inspect the input stream without consuming it, or assert conditions about the parse.
For these examples, we'll use:
let pA = pitem 'a'
let pB = pitem 'b'
let pLet = pseq "let"
Function |
Description |
Example Usage & Result |
---|---|---|
|
Runs |
|
|
Succeeds if |
|
|
Runs |
|
|
Asserts that |
|
Error Customization Combinators
Use these label operators to provide more context-friendly error messages.
Operator |
Description |
---|---|
|
If |
|
If |
let pIdentifier =
many1 (satisfy Char.IsLetter)
<?> "Expected a valid identifier"
// Running on "123" gives "Expected a valid identifier"
// Running on "abc!" gives "Unexpected '!'..." because the base parser consumed "abc" first.
val string: value: 'T -> string
--------------------
type string = System.String