Interface Parser<A, CTX, PROBLEM>

An advanced Parser gives two ways to improve your error messages:

  • problem — Instead of all errors being a string, you can create a custom type like type type 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:

import * as Advanced from "@honungsburk/kombo/Advanced"

type Context = {kind: "Definition", value: string } | { kind: "List" } | {kind : "Record" }

type Problem = {kind: "BadIndent" } | {kind: "BadKeyword", value: string}

type MyParser<A> = Advanced.Parser<A, Problem>

Remarks

Note that context isn't typed. I could not figure out how to both have it typed and get good type inference.

Type Parameters

  • A

    Success Value

  • CTX

  • PROBLEM

    When the parser fails it returns a problem

Hierarchy

  • Parser

Parsers

Mapping

  • Parse one thing andThen parse another thing. This is useful when you want to check on what you just parsed.

    Example

    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.

    See

    Type Parameters

    • B

    • CTX2

    • PROBLEM2

    Parameters

    Returns Parser.Parser<B, CTX | CTX2, PROBLEM | PROBLEM2>

  • Apply values to a function in a parser pipeline.

    Example

    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.

    See

    Type Parameters

    • CTX2

    • PROBLEM2

    Parameters

    Returns Parser.Parser<GetReturnType<A>, CTX | CTX2, PROBLEM | PROBLEM2>

  • Keep the return value of the parser it is given, and ignore the previous value.

    Remarks

    Just a shorthand for yourParser.andThen(() => ...)

    Example

    Maybe you want to first skip some whitespace and then grab an int.

    const whitespaceThenInt: Parser<number> = spaces.keep(int)
    

    Type Parameters

    • B

    • CTX2

    • PROBLEM2

    Parameters

    Returns Parser.Parser<B, CTX | CTX2, PROBLEM | PROBLEM2>

  • Transform the result of a parser.

    Example

    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 ...

    See

    • The infix version of map

    Type Parameters

    • B

    Parameters

    • fn: ((v: A) => B)
        • (v: A): B
        • Parameters

          • v: A

          Returns B

    Returns Parser.Parser<B, CTX, PROBLEM>

  • Skip the return value of the parser on the right-hand side.

    Example

     const integer =
    succeed(n => n)
    .skip(spaces)
    .apply(int)
    .skip(spaces)

    See

    Type Parameters

    • CTX2

    • PROBLEM2

    Parameters

    Returns Parser.Parser<A, CTX | CTX2, PROBLEM | PROBLEM2>

Branches

  • Just like Simple.oneOf but only between two parsers.

    Remarks

    NOTE: The left side is checked first!

    See

    Type Parameters

    • B

    • CTX2

    • PROBLEM2

    Parameters

    Returns Parser.Parser<A | B, CTX | CTX2, PROBLEM | PROBLEM2>

Chompers

Indentation

  • Just like Simple.withIndent

    Example

    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!

    Parameters

    • newIndent: number

    Returns Parser.Parser<A, CTX, PROBLEM>

Positions

Other

exec: ((s: State<unknown>) => PStep<A, CTX, PROBLEM>)

Type declaration

    • (s: State<unknown>): PStep<A, CTX, PROBLEM>
    • Internal

      WARNING: Do not use directly, it is used by the library

      Parameters

      Returns PStep<A, CTX, PROBLEM>