I have written a parser using megaparsec for a very simple language consisting of integer literals and two unary operators "a" and "b":
data ValueExpr = OpA ValueExpr
| OpB ValueExpr
| Integer Integer
valueExpr :: Parser ValueExpr
valueExpr = makeExprParser valueTerm valueOperatorTable
valueTerm :: Parser ValueExpr
valueTerm = parenthesised valueExpr
<|> Integer <$> integerLiteral
integerLiteral :: Parser Integer
integerLiteral = -- omitted
valueOperatorTable :: [[Operator Parser ValueExpr]]
valueOperatorTable = [[unaryOp "a" AOp,
unaryOp "b" BOp]]
parenthesised :: Parser a -> Parser a
parenthesised = between (char '(') (char ')')
unaryOp :: Text -> (a -> a) -> Operator Parser a
unaryOp name f = Prefix (f <$ symbol name)
binaryOp :: Text -> (a -> a -> a) -> Operator Parser a
binaryOp name f = InfixL (f <$ symbol name)
However, it seems that this doesn't allow me to "chain" unary operators, i.e. when trying to parse "ab1", I'm met with "unexpected 'b'". Why is that?
This is briefly mentioned in the documentation for makeExprParser
:
Unary operators of the same precedence can only occur once (i.e.,
--2
is not allowed if-
is prefix negate). If you need to parse several prefix or postfix operators in a row, ... you can use this approach:manyUnaryOp = foldr1 (.) <$> some singleUnaryOp
This is not done by default because in some cases allowing repeating prefix or postfix operators is not desirable.
In your specific example, something like the following ought to work:
valueOperatorTable :: [[Operator Parser ValueExpr]]
valueOperatorTable = [[Prefix unaryOps]]
unaryOps :: Parser (ValueExpr -> ValueExpr)
unaryOps = foldr1 (.) <$> some (OpA <$ symbol "a" <|> OpB <$ symbol "b")