Search code examples
parsinghaskellapplicativestate-monadalternative-functor

Haskell - Using State with Alternative


I have a datatype that looks like the following:

type Parser a = ExceptT ParseError (State [Token]) a

As well as state manipulation functions:

consumeToken :: Parser Token
consumeToken = do
    toks <- lift get
    if null toks 
        then throwE OutOfTokensError
        else 
            do
                lift $ put (tail toks)
                return $ head toks

peekToken :: Parser Token
peekToken = do
    toks <- lift get
    if null toks 
        then throwE OutOfTokensError
        else 
            do
                return $ head toks

I'm trying to use these functions to help validate production rules in a grammar:

charList :: Parser CharList
charList =
    (return CharListCharacter <*> isToken (Character "<char>") <*> charList)
    <|> (return CharListSpace <*> isToken (Space " ") <*> charList)
    <|> (return EmptyCharList)

It seems that isToken needs to consume the current token (using consumeToken) so that the recursive calls to charList then deal with the following token. However, doing so means that the alternative cases will not start from the same token as the first case.

Is there a standard way to deal with this problem?


Solution

  • Following the advice from the comments, I remodeled my parser to take advantage of the fact that my grammar is LL(1). Doing this meant I had no need for Alternative.

    This is the final version of the charList function (with isToken using consumeToken):

    charList :: Parser CharList
    charList = do
        tok <- peekToken
        case tok of Character _ -> return CharListCharacter <*> isToken (Character "<char>") <*> charList
                    Space _     -> return CharListSpace <*> isToken (Space " ") <*> charList
                    _           -> return EmptyCharList