build_primitive_parser : (input -> ParseResult(input, a)) -> Parser(input, a)
Write a custom parser without using the provided combinators.
Parser(input, a) :: # (opaque)
Generic parser combinators for transforming input into structured values.
Example:
Parse the following string from input into the structured output value:
input = "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green"
output =
{
id: 1,
requirements: [
[Blue(3), Red(4)],
[Red(1), Green(2), Blue(6)],
[Green(2)],
]
}
We could do this using the following:
Requirement : [Green(U64), Red(U64), Blue(U64)]
RequirementSet : List(Requirement)
Game : { id : U64, requirements : List(RequirementSet) }
parse_game : Str -> Try(Game, [ParsingError])
parse_game = |s| {
green = const(|x|Green(x)).keep(digits).skip(string(" green"))
red = const(|x|Red(x)).keep(digits).skip(string(" red"))
blue = const(|x|Blue(x)).keep(digits).skip(string(" blue"))
requirement_set : Parser(_, RequirementSet)
requirement_set = one_of([green, red, blue]).sep_by(string(", "))
requirements : Parser(_, List(RequirementSet))
requirements = requirement_set.sep_by(string("; "))
game : Parser(_, Game)
game = {
const(|id| |r| { id, requirements: r })
.skip(string("Game "))
.keep(digits)
.skip(string(": "))
.keep(requirements)
}
match String.parse_str(game, s) {
Ok(g) => Ok(g)
Err(ParsingFailure(_)) | Err(ParsingIncomplete(_)) => Err(ParsingError)
}
}
Opaque type for a parser that will try to parse an a from an input.
As such, a parser can be considered a recipe for a function of the type
input -> Try({val: a, input: input}, [ParsingFailure(Str)])
The representation is internal and may change to improve efficiency or error messages.
build_primitive_parser : (input -> ParseResult(input, a)) -> Parser(input, a)
Write a custom parser without using the provided combinators.
parse_partial : Parser(input, a), input -> ParseResult(input, a)
Most general way of running a parser.
Can be thought of as turning the recipe of a parser into its actual parsing function and running this function on the given input.
Most parsers consume part of input when they succeed. This allows you to string parsers
together that run one after the other. The part of the input that the first
parser did not consume, is used by the next parser.
This is why a parser returns on success both the resulting value and the leftover part of the input.
This is mostly useful when creating your own internal parsing building blocks.
parse : Parser(input, a), input, (input -> Bool) -> Try(a, [ParsingFailure(Str), ParsingIncomplete(input)])
Runs a parser on the given input, expecting it to fully consume the input
The input -> Bool parameter is used to check whether parsing has 'completed',
i.e. how to determine if all of the input has been consumed.
For most input types, a parsing run that leaves some unparsed input behind should be considered an error.
Parser that can never succeed, regardless of the given input. It will always fail with the given error message.
This is mostly useful as a 'base case' if all other parsers
in a one_of or alt have failed, to provide some more descriptive error message.
const : a -> Parser(_, a)
Parser that will always produce the given a, without looking at the actual input.
This is useful as a basic building block, especially in combination with
map and apply.
parse_u32 : Parser(List(U8), U32)
parse_u32 = {
const(U64.to_u32_wrap) # TODO: U64.to_u32_try would be better?
.keep(String.digits)
}
expect String.parse_str(parse_u32, "123") == Ok(123.U32)
alt : Parser(input, a), Parser(input, a) -> Parser(input, a)
Try the first parser and (only) if it fails, try the second parser as fallback.
apply : Parser(input, a -> b), Parser(input, a) -> Parser(input, b)
Runs a parser building a function, then a parser building a value, and finally returns the result of calling the function with the value.
This is useful if you are building up a structure that requires more parameters
than there are variants of map, map2, map3 etc. for.
For instance, the following two are the same:
const(|x| |y| |z| Triple(x, y, z))
.map3(String.digits, String.digits, String.digits)
const(|x| |y| |z| Triple(x, y, z))
.apply(String.digits)
.apply(String.digits)
.apply(String.digits)
Indeed, this is how map, map2, map3 etc. are implemented under the hood.
Currying:
Be aware that when using apply, you need to explicitly 'curry' the parameters to the construction function.
This means that instead of writing |x, y, z| ...
you'll need to write |x| |y| |z| ....
This is because the parameters of the function will be applied one by one as parsing continues.
Try a list of parsers in turn, until one of them succeeds.
color : Parser(Utf8, [Red, Green, Blue])
color = {
one_of(
[
const(Red).skip(string("red")),
const(Green).skip(string("green")),
const(Blue).skip(string("blue")),
],
)
}
expect String.parse_str(color, "green") == Ok(Green)
map : Parser(input, a), (a -> b) -> Parser(input, b)
Transforms the result of parsing into something else, using the given transformation function.
map2 : Parser(input, a), Parser(input, b), (a, b -> c) -> Parser(input, c)
Transforms the result of parsing into something else, using the given two-parameter transformation function.
map3 : Parser(input, a), Parser(input, b), Parser(input, c), (a, b, c -> d) -> Parser(input, d)
Transforms the result of parsing into something else, using the given three-parameter transformation function.
If you need transformations with more inputs,
take a look at apply.
Removes a layer of Result from running the parser.
Use this to map functions that return a result over the parser,
where errors are turned into ParsingFailures.
# Parse a number from a List(U8)
u64 : Parser(Utf8, U64)
u64 =
string
.map(
|val|
match U64.from_str(val) {
Ok(num) => Ok(num)
Err(_) => Err("${val} is not a U64."),
}
)
.flatten()
lazy : ({ } -> Parser(input, a)) -> Parser(input, a)
Runs a parser lazily
This is (only) useful when dealing with a recursive structure.
For instance, consider a type Comment : { message: String, responses: List(Comment) }.
Without lazy, you would ask the compiler to build an infinitely deep parser.
(Resulting in a compiler error.)
Mutually recursive top-level parser values currently require a lower-level workaround because of roc-lang/roc#10098.
A parser that tries to apply the given parser and returns Err(Nothing) if that parser fails.
A parser which runs the element parser *zero* or more times on the input, returning a list containing all the parsed elements.
one_or_more : Parser(input, a) -> Parser(input, List(a))
A parser which runs the element parser *one* or more times on the input, returning a list containing all the parsed elements.
Also see Parser.many.
between : Parser(input, a), Parser(input, open), Parser(input, close) -> Parser(input, a)
Runs a parser for an 'opening' delimiter, then your main parser, then the 'closing' delimiter, and only returns the result of your main parser.
Useful to recognize structures surrounded by delimiters (like braces, parentheses, quotes, etc.)
between_braces = |parser| parser.between(scalar('['), scalar(']'))
Parse one or more values separated by separator.
The separators are consumed and omitted from the result.
Parse zero or more values separated by separator.
The separators are consumed and omitted from the result.
parse_numbers : Parser(List(U8), List(U64))
parse_numbers = digits.sep_by(codeunit(','))
expect String.parse_str(parse_numbers, "1,2,3") == Ok([1, 2, 3])
ignore : Parser(input, a) -> Parser(input, { })
Discard a parser's value while preserving how much input it consumes.
keep : Parser(input, a -> b), Parser(input, a) -> Parser(input, b)
Run a parser producing a function, then a parser producing its argument, and return the function result.
skip : Parser(input, a), Parser(input, _) -> Parser(input, a)
Run two parsers in sequence, discarding the second parser's value.
chomp_until : a -> Parser(List(a), List(a)) where [a.is_eq : a, a -> Bool]
Match zero or more codeunits until it reaches the given codeunit. The given codeunit is not included in the match.
This can be used with Parser.skip to ignore text.
ignore_text : Parser(List(U8), U64)
ignore_text =
const(|d| d)
.skip(chomp_until(':'))
.skip(codeunit(':'))
.keep(digits)
expect String.parse_str(ignore_text, "ignore preceding text:123") == Ok(123)
This can be used with Parser.keep to capture a list of U8 codeunits.
capture_text : Parser(List(U8), List(U8))
capture_text =
const(|codeunits| codeunits)
.keep(chomp_until(':'))
.skip(codeunit(':'))
expect String.parse_str(capture_text, "Roc:") == Ok(['R', 'o', 'c'])
Use String.str_from_utf8 to turn the results into a Str.
Also see Parser.chomp_while.
chomp_while : (a -> Bool) -> Parser(List(a), List(a))
Match zero or more codeunits until the check returns false.
The codeunit that returned false is not included in the match.
Note: a chomp_while parser always succeeds!
This can be used with Parser.skip to ignore text.
This is useful for chomping whitespace or variable names.
ignore_numbers : Parser(List(U8), Str)
ignore_numbers =
const(|str| str)
.skip(chomp_while(|b| b >= '0' && b <= '9'))
.keep(string("TEXT"))
expect String.parse_str(ignore_numbers, "0123456789876543210TEXT") == Ok("TEXT")
This can be used with Parser.keep to capture a list of U8 codeunits.
capture_numbers : Parser(List(U8), List(U8))
capture_numbers =
const(|codeunits| codeunits)
.keep(chomp_while(|b| b >= '0' && b <= '9'))
.skip(string("TEXT"))
expect String.parse_str(capture_numbers, "123TEXT") == Ok(['1', '2', '3'])
Use String.str_from_utf8 to turn the results into a Str.
Also see Parser.chomp_until.
ParseResult : Try({ val : a, input : input }, [ParsingFailure(Str)])
The result of parsing part of an input: either a value and the remaining
input, or a ParsingFailure message.