如何使用Parsec解析三元表达式?

sin*_*ity 7 haskell parsec megaparsec

buildExpressionParser仅处理一元和二进制运算符。它可以处理像这样的三元运算符?:吗?这里这里都有一些讨论,但没有一个是结论性的。

tha*_*guy 7

单子语法分析器的一大优点是它们的组合非常好。

这意味着您不必尝试欺骗任何人buildExpressionParser来构建所需的确切解析器。您可以简单地使用它来构建解析器,然后像其他任何东西一样使用该解析器,包括another buildExpressionParser

就您而言,您可以:

  1. 用于buildExpressionParser为优先级高于的表达式创建解析器?:
  2. 创建一个解析器以?:使用上述内容
  3. 用于buildExpressionParser为优先级低于?:使用上述表达式的表达式创建解析器

这是一个完整的示例:

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"
Run Code Online (Sandbox Code Playgroud)

输出(手动格式化)为:

Right 
  (Assign 
    (Var "a") 
    (Trinary 
      (Add (Var "b") 
           (Mul (Var "c") (Var "d"))) 
      (Add (Var "e") (Var "f")) 
      (Mul (Var "g") (Var "h"))))
Run Code Online (Sandbox Code Playgroud)