Techniques for debugging Parsers?

I learned I struggle a lot with figuring out where have my various elm/parser parsers gone wrong.

So far I probably only have two techniques:


Print the dead ends as nicely as possible
expectEqualParseResult :
    String
    -> Result (List (P.DeadEnd ParseContext ParseProblem)) a
    -> Result (List (P.DeadEnd ParseContext ParseProblem)) a
    -> Expectation
expectEqualParseResult input expected actual =
    if actual == expected then
        Expect.pass

    else
        case actual of
            Err deadEnds ->
                Expect.fail
                    (String.join "\n"
                        (input
                            :: "===>"
                            :: "Err"
                            :: List.map deadEndToString deadEnds
                        )
                    )

            _ ->
                actual |> Expect.equal expected


deadEndToString : P.DeadEnd ParseContext ParseProblem -> String
deadEndToString deadEnd =
    let
        metadata =
            "("
                ++ String.fromInt (deadEnd.row - 1)
                ++ ","
                ++ String.fromInt (deadEnd.col - 1)
                ++ ") "
                ++ Debug.toString deadEnd.problem
    in
    String.join "\n    "
        ("\n"
            :: metadata
            :: "---- with context stack ----"
            :: List.map contextToString deadEnd.contextStack
        )


contextToString : { row : Int, col : Int, context : ParseContext } -> String
contextToString context =
    "("
        ++ String.fromInt (context.row - 1)
        ++ ","
        ++ String.fromInt (context.col - 1)
        ++ ") "
        ++ Debug.toString context.context
Example result
↓ ParserTest
↓ Stage.Parse.Parser.expr
↓ literal string
✗ empty

    ""
    ===>
    Err


        (0,2) ExpectingDoubleQuote
        ---- with context stack ----
        (0,0) InString
        (0,0) InLiteral
        (0,0) InExpr


↓ ParserTest
↓ Stage.Parse.Parser.expr
↓ literal string
✗ one space

    " "
    ===>
    Err


        (0,3) ExpectingDoubleQuote
        ---- with context stack ----
        (0,0) InString
        (0,0) InLiteral
        (0,0) InExpr


↓ ParserTest
↓ Stage.Parse.Parser.expr
↓ literal string
✗ two numbers

    "42"
    ===>
    Err


        (0,4) ExpectingDoubleQuote
        ---- with context stack ----
        (0,0) InString
        (0,0) InLiteral
        (0,0) InExpr

`log` parser combinator
log : String -> Parser_ a -> Parser_ a
log message parser =
    P.succeed ()
        |> P.andThen
            (\() ->
                let
                    _ =
                        Debug.log "starting" message
                in
                P.succeed
                    (\source offsetBefore parseResult offsetAfter ->
                        let
                            _ =
                                Debug.log "-----------------------------------------------" message

                            _ =
                                Debug.log "source         " source

                            _ =
                                Debug.log "chomped string " (String.slice offsetBefore offsetAfter source)

                            _ =
                                Debug.log "parsed result  " parseResult
                        in
                        parseResult
                    )
                    |= P.getSource
                    |= P.getOffset
                    |= parser
                    |= P.getOffset
            )
Example result
starting: "many"
-----------------------------------------------: "many"
source         : "\"42\""
chomped string : "42\""
parsed result  : ['4','2','"']
starting: "many"
starting: "many"
-----------------------------------------------: "many"
source         : "\"\\\"\""
chomped string : "\\\"\""
parsed result  : ['"','"']
starting: "many"
starting: "many"

What are yours? What do you usually do when you need to debug a Parser?

9 Likes

What I feel might be very benefitial is some sort of Parser.debugRun which would return a trace of what parser ate what characters. Might disprove a lot of debugging hypotheses fast and give good insight into what’s actually happening :slight_smile:

5 Likes