/logic/Propositions.hs

https://github.com/nomeata/incredible · Haskell · 205 lines · 154 code · 41 blank · 10 comment · 3 complexity · 72fba57acf5484b69a9498ccfe11187c MD5 · raw file

  1. {-# LANGUAGE TemplateHaskell, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts, UndecidableInstances #-}
  2. {-# LANGUAGE TypeFamilies #-} -- for the ~ type signature trick
  3. module Propositions (module Propositions, string2Name) where
  4. import Text.Parsec
  5. import Text.Parsec.Expr
  6. import Text.Parsec.String
  7. import Data.Functor.Identity
  8. import Control.Monad
  9. import Data.List
  10. import Utils
  11. import Unbound.LocallyNameless hiding (Infix)
  12. type Var = Name Term
  13. -- This could be made more abstract as in the unification-fd package
  14. data Term
  15. = App Term [Term]
  16. | V Var
  17. | C Var
  18. | Lam (Bind Var Term)
  19. deriving Show
  20. $(derive [''Term])
  21. data OpAssoc
  22. = L
  23. | R
  24. instance Alpha Term
  25. instance Eq Term where (==) = aeq
  26. instance Subst Term Term where
  27. isvar (V v) = Just (SubstName v)
  28. isvar (C v) = Just (SubstName v)
  29. isvar _ = Nothing
  30. firstFree :: Alpha a => a -> Integer
  31. firstFree = (1+) . maximum . (0:) . map anyName2Integer . fvAny
  32. type Proposition = Term
  33. absTerm :: [Var] -> Term -> Term
  34. absTerm vs t = foldr mkLam t vs
  35. mkLam :: Var -> Term -> Term
  36. mkLam v t = Lam (bind v (const2Var [v] t))
  37. mkApps :: Term -> [Term] -> Term
  38. mkApps t [] = t
  39. mkApps t ts = App t ts
  40. const2Var :: [Var] -> Term -> Term
  41. const2Var vs = substs [(v, V v) | v <- vs]
  42. name2ExternalString :: Var -> String
  43. name2ExternalString n
  44. | name2Integer n == 0 = name2String n
  45. | otherwise = error $ "name2ExternalString: Invalid external name " ++ show n
  46. -- Pretty printer
  47. printTerm :: Proposition -> String
  48. printTerm p = runLFreshM (avoid (fvAny p) $ prP (0::Int) p) ""
  49. where
  50. prP :: Int -> Proposition -> LFreshM (String -> String)
  51. prP _ (C v) = prN v
  52. prP _ (V v) = prN v
  53. prP d (App (C f) [a]) | Just p <- isPrefix (name2String f)
  54. = prParens (p < d) $ prN f <> prP p a
  55. prP d (App (C f) [a1, a2]) | Just (p,_assoc) <- isInFix (name2String f)
  56. = prParens (p < d) $ prP (p+1) a1 <> prN f <> prP (p+1) a2
  57. prP d (App (C f) [Lam b]) | isQuant (name2String f)
  58. = prParens (1 < d) $ lunbind b $ \(v,t) ->
  59. prN f <> prN v <> prS "." <> prP 1 t
  60. prP _ (App f args) = prP 4 f <> prS "(" <> prCommas [prP 0 a | a <- args] <> prS ")"
  61. prP d (Lam b) = prParens (1 < d) $ lunbind b $ \(v,t) ->
  62. prS "Λ" <> prN v <> prS "." <> prP 1 t
  63. prN n = prS (name2String n)
  64. <> (if i > 0 then prS (map subscriptify (show i)) else return id)
  65. where i = name2Integer n
  66. prParens :: Bool -> LFreshM (String -> String) -> LFreshM (String -> String)
  67. prParens True f = prS "(" <> f <> prS ")"
  68. prParens False f = f
  69. prCommas = foldr (<>) (return id) . intersperse (prS ",")
  70. prS str = return (str++)
  71. (<>) :: t ~ LFreshM (String -> String) => t -> t -> t
  72. (<>) = liftM2 (.)
  73. -- Is it infix? What precedences?
  74. isInFix :: String -> Maybe (Int, OpAssoc)
  75. isInFix "⋅" = Just (7, L)
  76. isInFix "↑" = Just (5, L)
  77. isInFix "∧" = Just (4, L)
  78. isInFix "∨" = Just (4, L)
  79. isInFix "→" = Just (3, R)
  80. isInFix ":" = Just (2, R)
  81. isInFix _ = Nothing
  82. isQuant :: String -> Bool
  83. isQuant = (`elem` words "∃ ∀ λ")
  84. isPrefix :: String -> Maybe Int
  85. isPrefix "¬" = Just 6
  86. isPrefix _ = Nothing
  87. -- Parser
  88. parseTerm :: String -> Either String Proposition
  89. parseTerm = either (Left . show) Right . parse (between spaces eof termP) ""
  90. -- For Testing and GHCi
  91. readTerm :: String -> Proposition
  92. readTerm = either (error . show) id . parseTerm
  93. -- lexeme
  94. l :: Parser a -> Parser a
  95. l = (<* (spaces <?> ""))
  96. termP :: Parser Proposition
  97. termP = buildExpressionParser table atomP <?> "proposition"
  98. table :: OperatorTable String () Identity Proposition
  99. table = [ [ binary "⋅" [] AssocLeft
  100. ]
  101. , [ binary "↑" ["^"] AssocLeft
  102. ]
  103. , [ binary "∧" ["&"] AssocLeft
  104. ]
  105. , [ binary "∨" ["|"] AssocLeft
  106. ]
  107. , [ binary "→" ["->"] AssocRight
  108. ]
  109. , [ binary ":" [] AssocRight
  110. ]
  111. ]
  112. where
  113. binary op alts assoc = Infix ((\a b -> App (C (string2Name op)) [a,b]) <$ l (choice (map string (op:alts)))) assoc
  114. quantifiers :: [(Char, [Char])]
  115. quantifiers =
  116. [ ('∀', ['!'])
  117. , ('∃', ['?'])
  118. , ('λ', ['\\'])
  119. , ('Λ', [])
  120. ]
  121. mkQuant :: String -> Var -> Term -> Term
  122. mkQuant "Λ" n t = mkLam n t
  123. mkQuant q n t = App (C (string2Name q)) [mkLam n t]
  124. quantP :: Parser String
  125. quantP = l $ choice [ (q:"") <$ choice (map char (q:a)) | (q,a) <- quantifiers ]
  126. atomP :: Parser Proposition
  127. atomP = choice
  128. [ l $ string "⊥" >> return (c "⊥")
  129. , l $ string "⊤" >> return (c "⊤")
  130. , l $ try (string "False") >> return (c "⊥")
  131. , l $ try (string "True") >> return (c "⊤")
  132. , do
  133. _ <- l $ char '¬' <|> char '~'
  134. p <- atomP
  135. return $ s "¬" [p]
  136. , do
  137. q <- quantP
  138. vname <- nameP
  139. _ <- l $ char '.'
  140. p <- termP
  141. return $ mkQuant q vname $ p
  142. , parenthesized termP
  143. , do
  144. head <- varOrConstP
  145. option head $ parenthesized $
  146. App head <$> termP `sepBy1` (l $ char ',')
  147. ]
  148. where
  149. c n = C (string2Name n)
  150. s n = App (c n)
  151. parenthesized = between (l $ char '(') (l $ char ')')
  152. varOrConstP :: Parser Term
  153. varOrConstP = choice
  154. [ do
  155. -- A hack for the test suite etc: prepending the name of a constant with V
  156. -- makes it a variable
  157. _ <- try (string "V ")
  158. num <- l $ option 0 (read <$> many1 digit)
  159. s <- l $ many1 alphaNum
  160. return $ V $ makeName s num
  161. , C <$> nameP
  162. ]
  163. nameP :: Rep a => Parser (Name a)
  164. nameP = l $ string2Name <$> many1 (alphaNum <|> char '_')