How to use an Alex monadic lexer with Happy?

前端 未结 1 625
天命终不由人
天命终不由人 2020-12-09 19:50

I\'m trying to learn using Alex + Happy to build parser, in particular I\'m interested in learning to use the monad wrapper of Alex. I have already looked at th

相关标签:
1条回答
  • 2020-12-09 19:57

    Your lexer's definition is completely fine as far as I can tell. Assuming there are no bugs there, the only problems you need to fix are in your parser's configuration. The first thing is that the lexer you are using is the wrong one. While that function is the interface to the Alex lexer, it has the type

    alexMonadScan :: Alex result
    

    But the lexer Happy wants is of type

    lexer :: (Token -> P a) -> P a
    

    Where P is the monad we are using. What this is saying is that the lexer should provide us an Alex a when given a continuation. A simple wrapper is what we need here:

    lexwrap :: (Token -> Alex a) -> Alex a
    lexwrap cont = do
        token <- alexMonadScan
        cont token
    

    or equivalently

    lexwrap = (alexMonadScan >>=)
    

    Second, using alexEOF in the %lexer directive will cause your parser to fail on every input. The name you supply there is inserted in to a branch of a case statement in generated code, so you must use the name of a data constructor rather than a value --- in particular, you need to use the data constructor that Alex will emit to signal EOF.

    This makes our lexer line in the parser a little different.

    %lexer {lexwrap} {Eof}
    

    (As a side note, this is the reason that you need to write alexEOF = return Eof yourself. The data constructor you return inside alexEOF needs to pattern-match against the data constructor you identify to Happy as the one that ends the file. Alex has no way of knowing what you want to emit, and Happy has no way of knowing what you chose to emit via Alex.)

    Now the next problem is that your parseError's type is incorrect. When using just a monad, that is indeed the type you need, but when you add a lexer into the mix, your parseError must have a different type. Also, using fail is probably not advised, so here is a slightly better definition:

    parseError :: Token -> Alex a
    parseError _ = alexError "Why is using happy and alex so hard"
    

    Finally, the main function is definied a little strange here. what we want to do to call the parser is to invoke it with runAlex. So here is a quick wrapper for it. The string passed in is the string that you wish to parse.

    parse :: String -> Either String [Section]
    parse s = runAlex s parseIniFile
    

    The type of the function parse is determined by the parseIniFile's definition. Here, it is an Alex [Section] so an Either String [Section] is returned.

    I think that's everything.

    0 讨论(0)
提交回复
热议问题