Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse ternary expression using Parsec?

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.

like image 931
sinoTrinity Avatar asked Sep 13 '19 21:09

sinoTrinity


1 Answers

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"))))
like image 68
that other guy Avatar answered Oct 25 '22 15:10

that other guy