buildExpressionParser only deals with unary and binary operators. Can it handle ternary operators like ?:
? There are some discussions here and here, but none is conclusive.
Asked
Active
Viewed 324 times
8

sinoTrinity
- 1,125
- 2
- 15
- 27
-
A work around could be to see `x ? y : z` as two operators, so something like `x ? (y : z)`. As long as you pass correct objects, etc. That is not per se a huge problem. You can use a `Maybe`, etc. to denote failure. But as the article says, building a parser with `Parsec` itself is not that hard. – Willem Van Onsem Sep 13 '19 at 21:59
1 Answers
8
One of the great benefits of monadic parsers is that they compose really well.
This means that you don't have to try to trick a single buildExpressionParser
into building the exact parser you want. You can simply use it to build a parser, and then use that parser like any other for anything, including another buildExpressionParser
.
In your case, you can:
- Use
buildExpressionParser
to create a parser for expressions with higher precedence than?:
- Create a parser for
?:
using the above - Use
buildExpressionParser
to create a parser for expressions with lower precedence than?:
using the above
Here's a complete example:
import Control.Monad.Identity
import Text.Parsec
import Text.Parsec.Expr
data Ex = Var String | Mul Ex Ex | Add Ex Ex | Assign Ex Ex | Trinary Ex Ex Ex
deriving (Show)
var = Var <$> many1 letter
topOps = [ [binary "*" Mul], [binary "+" Add] ]
topParser = buildExpressionParser topOps var
trinaryExpr = do
expr <- topParser
trinary expr <|> return expr
where
trinary expr = do
string "?"
thenV <- trinaryExpr
string ":"
elseV <- trinaryExpr
return $ Trinary expr thenV elseV
bottomOps = [ [ binary "=" Assign ] ]
bottomParser = buildExpressionParser bottomOps trinaryExpr
binary :: String -> (Ex -> Ex -> Ex) -> Operator String () Identity Ex
binary s t = Infix (string s >> return t) AssocLeft
testParse s = runParser bottomParser () "" s
main = print $ testParse "a=b+c*d?e+f:g*h"
The output (manually formatted) is:
Right
(Assign
(Var "a")
(Trinary
(Add (Var "b")
(Mul (Var "c") (Var "d")))
(Add (Var "e") (Var "f"))
(Mul (Var "g") (Var "h"))))

that other guy
- 116,971
- 11
- 170
- 194