8

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.

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 Answers1

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:

  1. Use buildExpressionParser to create a parser for expressions with higher precedence than ?:
  2. Create a parser for ?: using the above
  3. 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