Common Parsers
Primitive parsers are the fundamental building blocks of XParsec. They are the functions that directly interact with the input stream by consuming items, or with the parser's environment by inspecting its state and position. Every complex parser is ultimately built by combining these primitives.
This page lists the core parsers available in the XParsec.Parsers
module.
How to Read the Examples
The examples below show how to call each parser. To see what they do, you can run them with a simple test function like this:
open XParsec
// A helper to run a parser and print its result
let testParser parser input =
match parser (Reader.ofString input ()) with
| Ok success -> printfn $"Input: \"{input}\" -> Ok {success.Parsed}"
| Error err ->
let errorMsg = ErrorFormatting.formatStringError input err
printfn $"Input: \"{input}\" -> Error:\n{errorMsg}"
// Example usage:
testParser (pitem 'a') "abc"
// Prints: Input: "abc" -> Ok a
General-Purpose Parsers
These are the most basic parsers for controlling success and failure.
Parser |
Description |
Example Usage |
---|---|---|
|
Always succeeds with the value |
|
|
Always fails with a generic error. Useful as an identity for |
|
|
Always fails with a specific error message |
|
|
Succeeds (returning |
|
|
Consumes and returns a single item of any kind. Fails at the end of input. |
|
Item and Sequence Parsers
These parsers match specific items or sequences in the input.
Parser |
Description |
Example Usage |
---|---|---|
|
Parses a single item |
|
|
Parses and discards a single item |
|
|
Parses a sequence of items |
|
|
Parses sequence |
|
|
Parses a single item that satisfies the predicate |
|
|
Parses any single item from the sequence |
|
|
Parses any single item not in the sequence |
|
|
Like |
|
|
Parses any single item within the inclusive range. |
|
|
Like |
|
State and Position Parsers
These parsers allow you to interact with the parser's environment: the user-defined state and the reader's current position. This is a powerful feature for context-sensitive parsing, such as tracking indentation levels in Python or managing a symbol table in a compiler.
For many parsers with simple grammars, a custom state is unnecessary. In these cases, you can simply use unit
as the state type, and the state-related functions can be ignored.
Parser |
Description |
Example Usage |
---|---|---|
|
Returns the current user-defined state. |
|
|
Sets the user state to the new value |
|
|
Applies function |
|
|
Succeeds if the current state satisfies predicate |
|
|
Returns the current |
|
|
Jumps the parser to a previously saved |
|
Example: Using State to Count
Let's create a parser that consumes exactly N
occurrences of the character 'a', where N
is read from the initial state.
// This parser uses an int as its state, representing a counter.
let pConsumeAs (state: int) =
// A parser that consumes a single 'a' and decrements the counter.
let pSingleA = parser {
do! pitem 'a'
do! updateUserState (fun i -> i - 1)
}
// A parser that succeeds only when the counter is zero.
let pCheckCounter = userStateSatisfies ((=) 0)
// The full parser: repeat pSingleA `state` times, then check the counter.
skipArray state pSingleA >>. pCheckCounter
// Let's run it with an initial state of 3.
testParser (pConsumeAs 3) "aaa"
// Input: "aaa" -> Ok ()
testParser (pConsumeAs 3) "aa"
// Input: "aa" -> Error:
// --> 1:3
// |
// 1 | aa
// | ^
// | Unexpected end of input. Expected 'a'.
Example: Manual Backtracking with Position
getPosition
and setPosition
are the tools for manual backtracking. You can "try" a parse and then reset the stream's position if you decide to do something else.
// Let's parse an identifier, but if it's "let", we want to parse "let-binding" instead.
let pIdentifier = many1Chars (satisfy Char.IsLower)
// This parser demonstrates a manual lookahead.
let pLookahead = parser {
// 1. Save our current position.
let! startPos = getPosition
// 2. Try parsing an identifier.
let! id = pIdentifier
// 3. Check if the identifier is "let".
if id = "let" then
// It is! We don't want the simple identifier.
// 4. Reset the stream to where we started.
do! setPosition startPos
// 5. Run a different parser.
return! pseq "let-binding"
else
// It's not "let", so the simple identifier is fine.
return id
}
testParser pLookahead "let-binding"
// Input: "let-binding" -> Ok let-binding
testParser pLookahead "variable"
// Input: "variable" -> Ok variable
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int