Success Value
When the parser fails it returns a problem
Parse one thing andThen
parse another thing. This is useful when you want
to check on what you just parsed.
Maybe you want U.S. zip codes
and int
is not suitable because it does not allow leading zeros. You could
say:
const checkZipCode = (code: string): Parser<string> => {
if (code.length === 5) {
return succeed(code);
} else {
return problem("a U.S. zip code has exactly 5 digits");
}
};
const zipCode: Parser<string> = chompWhile(Helpers.isDigit)
.getChompedString()
.andThen(checkZipCode);
First we chomp digits andThen
we check if it is a valid U.S. zip code. We
succeed
if it has exactly five digits and reports a problem
if not.
Note: If you are using andThen
recursively and blowing the stack, check
out the loop function to limit stack usage.
Apply values to a function in a parser pipeline.
If we want to parse some kind of Point
type we could say:
type Point = {
x: number;
y: number;
};
const createPoint =
(x: number) =>
(y: number): Point => ({ x, y });
// A parser pipeline
const point: Parser<Point> = succeed(createPoint)
.skip(symbol("("))
.skip(spaces)
.apply(float)
.skip(symbol(","))
.skip(spaces)
.apply(float)
.skip(spaces)
.skip(symbol(")"));
NOTE: You can only use .apply(...)
on a parser that contains a
function that is curried.
Keep the return value of the parser it is given, and ignore the previous value.
Just a shorthand for yourParser.andThen(() => ...)
Maybe you want to first skip some whitespace and then grab an int.
const whitespaceThenInt: Parser<number> = spaces.keep(int)
Transform the result of a parser.
Maybe you have a value that is an integer or null
:
const nullOrInt: Parser<number | undefined> = oneOf(
int,
keyword("null").map((_) => undefined)
);
// run(nullOrInt)("0") ==> Ok(0)
// run(nullOrInt)("13") ==> Ok(13)
// run(nullOrInt)("null") ==> Ok(undefined)
// run(nullOrInt)("zero") ==> Err ...
Skip the return value of the parser on the right-hand side.
const integer =
succeed(n => n)
.skip(spaces)
.apply(int)
.skip(spaces)
Just like Simple.backtrackable
Just like Simple.oneOf but only between two parsers.
NOTE: The left side is checked first!
Just like Simple.getChompedString
Just like Simple.mapChompedString
Just like Simple.getIndent
Writing yourParser.getIndent()
is the same as yourParser.keep(getIndent)
Just like Simple.withIndent
Note: withIndent
sets the indentation for all previous uses of getIndent
const parser = succeed(Unit).withIndent(3).getIndent();
run(parser)(""); // => 1
const parser2 = succeed(Unit).getIndent().withIndent(3);
run(parser2)(""); // => 3
This might at first look seem strange, but a good way to think about it is that our parser is a tree-like structure
withindent(4)withindent(4) parser3 | | | |__ parser4 | |parser2 ___ parser5 | | parser6 | |__ parser7
where withIndent
only applies to its subtrees!
Just like Simple.getCol
Just like Simple.getPosition
Just like Simple.getRow
An advanced Parser gives two ways to improve your error messages:
problem
— Instead of all errors being astring
, you can create a custom type like typetype Problem = BadIndent | BadKeyword
and track problems much more precisely.context
— Error messages can be further improved when precise problems are paired with information about where you ran into trouble. By tracking the context, instead of saying “I found a bad keyword” you can say “I found a bad keyword when parsing a list” and give folks a better idea of what the parser thinks it is doing.I recommend starting with the simpler Parser module, and when you feel comfortable and want better error messages, you can create a type alias like this:
Remarks
Note that
context
isn't typed. I could not figure out how to both have it typed and get good type inference.