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