在Haskell中解析形式逻辑

时间:2018-11-20 12:41:01

标签: parsing haskell parsec

我尝试解析以下语言。

formula ::=  true  
         |  false  
         |  var  
         |  formula & formula  
         |  ∀ var . formula
         |  (formula)

    var ::=  letter { letter | digit }*

我一直在Haskell Wiki上关注this article,并使用Parsec组合器来创建以下解析器。

module LogicParser where

import System.IO
import Control.Monad
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
import Text.ParserCombinators.Parsec.Language
import qualified Text.ParserCombinators.Parsec.Token as Token

-- Data Structures
data Formula = Var String
             | TT
             | FF
             | Con Formula Formula
             | Forall String Formula
               deriving (Show)

-- Language Definition
lang :: LanguageDef st
lang =
    emptyDef{ Token.identStart      = letter
            , Token.identLetter     = alphaNum
            , Token.opStart         = oneOf "&."
            , Token.opLetter        = oneOf "&."
            , Token.reservedOpNames = ["&", "."]
            , Token.reservedNames   = ["tt", "ff", "forall"]
            }


-- Lexer for langauge
lexer = 
    Token.makeTokenParser lang


-- Trivial Parsers
identifier     = Token.identifier lexer
keyword        = Token.reserved lexer
op             = Token.reservedOp lexer
roundBrackets  = Token.parens lexer
whiteSpace     = Token.whiteSpace lexer

-- Main Parser, takes care of trailing whitespaces
formulaParser :: Parser Formula
formulaParser = whiteSpace >> formula

-- Parsing Formulas
formula :: Parser Formula
formula = conFormula
        <|> formulaTerm
        <|> forallFormula

-- Term in a Formula
formulaTerm :: Parser Formula
formulaTerm = roundBrackets formula
            <|> ttFormula
            <|> ffFormula
            <|> varFormula

-- Conjunction
conFormula :: Parser Formula
conFormula = 
    buildExpressionParser [[Infix (op "&" >> return Con) AssocLeft]] formula

-- Truth
ttFormula :: Parser Formula
ttFormula = keyword "tt" >> return TT

-- Falsehood
ffFormula :: Parser Formula
ffFormula = keyword "ff" >> return FF

-- Variable
varFormula :: Parser Formula
varFormula =
    do  var <- identifier
        return $ Var var

-- Universal Statement 
forallFormula :: Parser Formula
forallFormula = 
    do  keyword "forall"
        x <- identifier
        op "."
        phi <- formulaTerm
        return $ Forall x phi

-- For running runghc
calculate :: String -> String
calculate s = 
    case ret of
        Left e ->  "Error: " ++ (show e)
        Right n -> "Interpreted as: " ++ (show n)
    where 
        ret = parse formulaParser "" s

main :: IO ()
main = interact (unlines . (map calculate) . lines)

问题是&运算符,我正在尝试使用Infix函数在how the article parses expressions上建模,并向其传递一个运算符列表和一个解析器以解析术语。但是,解析器的行为不符合预期,我无法弄清原因。以下是一些预期行为的示例:

true                         -- parsing -->      TT
true & false                 -- parsing -->      Con TT FF
true & (true & false)        -- parsing -->      Con TT (Con TT FF)
forall x . false & true      -- parsing -->      Con (Forall "x" FF) TT

但是,当前解析器未产生任何输出。感谢您的协助。

2 个答案:

答案 0 :(得分:1)

我认为我通过改写语法设法解决了这个问题,并且连词仍然是左联想的:

formula :: Parser Formula
formula = conFormula
        <|> formulaTerm

formulaTerm :: Parser Formula
formulaTerm = roundBrackets formula
            <|> ttFormula
            <|> ffFormula
            <|> varFormula
            <|> forallFormula

conFormula :: Parser Formula
conFormula = 
    buildExpressionParser [[Infix (op "&" >> return Con) AssocLeft]] formulaTerm

这成功解析了以下内容。 enter image description here

答案 1 :(得分:0)

您的代码中的问题是,您尝试解析formula,而其首次尝试是解析formulaCon。然后,这首先尝试解析formula,即您创建一个无限递归,而不会消耗任何输入

要解决此问题,您必须构建语法。定义这样的术语(请注意,所有这些术语在递归回到formula之前都会消耗一些输入):

formulaTerm = ttFormula
          <|> ffFormula
          <|> varFormula
          <|> forallFormula
          <|> roundBrackets formula

forallFormula = do
  keyword "forall"
  x <- identifier
  op "."
  phi <- formula
  return $ Forall x phi

那么,一个公式可以是单个术语,也可以是由术语,运算符和另一个公式组成的连词。为了确保所有输入都被解析,请首先尝试解析一个连词,如果失败,则解析一个词:

formula = (try formulaCon) <|> formulaTerm

最后可以像这样解析formulaCon

formulaCon = do
  f1 <- formulaTerm
  op "&"
  f2 <- formula
  return $ Con f1 f2

此解决方案的缺点是现在连接是正确的关联。