/logic/Propositions.hs
https://github.com/nomeata/incredible · Haskell · 205 lines · 154 code · 41 blank · 10 comment · 3 complexity · 72fba57acf5484b69a9498ccfe11187c MD5 · raw file
- {-# LANGUAGE TemplateHaskell, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts, UndecidableInstances #-}
- {-# LANGUAGE TypeFamilies #-} -- for the ~ type signature trick
- module Propositions (module Propositions, string2Name) where
- import Text.Parsec
- import Text.Parsec.Expr
- import Text.Parsec.String
- import Data.Functor.Identity
- import Control.Monad
- import Data.List
- import Utils
- import Unbound.LocallyNameless hiding (Infix)
- type Var = Name Term
- -- This could be made more abstract as in the unification-fd package
- data Term
- = App Term [Term]
- | V Var
- | C Var
- | Lam (Bind Var Term)
- deriving Show
- $(derive [''Term])
- data OpAssoc
- = L
- | R
- instance Alpha Term
- instance Eq Term where (==) = aeq
- instance Subst Term Term where
- isvar (V v) = Just (SubstName v)
- isvar (C v) = Just (SubstName v)
- isvar _ = Nothing
- firstFree :: Alpha a => a -> Integer
- firstFree = (1+) . maximum . (0:) . map anyName2Integer . fvAny
- type Proposition = Term
- absTerm :: [Var] -> Term -> Term
- absTerm vs t = foldr mkLam t vs
- mkLam :: Var -> Term -> Term
- mkLam v t = Lam (bind v (const2Var [v] t))
- mkApps :: Term -> [Term] -> Term
- mkApps t [] = t
- mkApps t ts = App t ts
- const2Var :: [Var] -> Term -> Term
- const2Var vs = substs [(v, V v) | v <- vs]
- name2ExternalString :: Var -> String
- name2ExternalString n
- | name2Integer n == 0 = name2String n
- | otherwise = error $ "name2ExternalString: Invalid external name " ++ show n
- -- Pretty printer
- printTerm :: Proposition -> String
- printTerm p = runLFreshM (avoid (fvAny p) $ prP (0::Int) p) ""
- where
- prP :: Int -> Proposition -> LFreshM (String -> String)
- prP _ (C v) = prN v
- prP _ (V v) = prN v
- prP d (App (C f) [a]) | Just p <- isPrefix (name2String f)
- = prParens (p < d) $ prN f <> prP p a
- prP d (App (C f) [a1, a2]) | Just (p,_assoc) <- isInFix (name2String f)
- = prParens (p < d) $ prP (p+1) a1 <> prN f <> prP (p+1) a2
- prP d (App (C f) [Lam b]) | isQuant (name2String f)
- = prParens (1 < d) $ lunbind b $ \(v,t) ->
- prN f <> prN v <> prS "." <> prP 1 t
- prP _ (App f args) = prP 4 f <> prS "(" <> prCommas [prP 0 a | a <- args] <> prS ")"
- prP d (Lam b) = prParens (1 < d) $ lunbind b $ \(v,t) ->
- prS "Λ" <> prN v <> prS "." <> prP 1 t
- prN n = prS (name2String n)
- <> (if i > 0 then prS (map subscriptify (show i)) else return id)
- where i = name2Integer n
- prParens :: Bool -> LFreshM (String -> String) -> LFreshM (String -> String)
- prParens True f = prS "(" <> f <> prS ")"
- prParens False f = f
- prCommas = foldr (<>) (return id) . intersperse (prS ",")
- prS str = return (str++)
- (<>) :: t ~ LFreshM (String -> String) => t -> t -> t
- (<>) = liftM2 (.)
- -- Is it infix? What precedences?
- isInFix :: String -> Maybe (Int, OpAssoc)
- isInFix "⋅" = Just (7, L)
- isInFix "↑" = Just (5, L)
- isInFix "∧" = Just (4, L)
- isInFix "∨" = Just (4, L)
- isInFix "→" = Just (3, R)
- isInFix ":" = Just (2, R)
- isInFix _ = Nothing
- isQuant :: String -> Bool
- isQuant = (`elem` words "∃ ∀ λ")
- isPrefix :: String -> Maybe Int
- isPrefix "¬" = Just 6
- isPrefix _ = Nothing
- -- Parser
- parseTerm :: String -> Either String Proposition
- parseTerm = either (Left . show) Right . parse (between spaces eof termP) ""
- -- For Testing and GHCi
- readTerm :: String -> Proposition
- readTerm = either (error . show) id . parseTerm
- -- lexeme
- l :: Parser a -> Parser a
- l = (<* (spaces <?> ""))
- termP :: Parser Proposition
- termP = buildExpressionParser table atomP <?> "proposition"
- table :: OperatorTable String () Identity Proposition
- table = [ [ binary "⋅" [] AssocLeft
- ]
- , [ binary "↑" ["^"] AssocLeft
- ]
- , [ binary "∧" ["&"] AssocLeft
- ]
- , [ binary "∨" ["|"] AssocLeft
- ]
- , [ binary "→" ["->"] AssocRight
- ]
- , [ binary ":" [] AssocRight
- ]
- ]
- where
- binary op alts assoc = Infix ((\a b -> App (C (string2Name op)) [a,b]) <$ l (choice (map string (op:alts)))) assoc
- quantifiers :: [(Char, [Char])]
- quantifiers =
- [ ('∀', ['!'])
- , ('∃', ['?'])
- , ('λ', ['\\'])
- , ('Λ', [])
- ]
- mkQuant :: String -> Var -> Term -> Term
- mkQuant "Λ" n t = mkLam n t
- mkQuant q n t = App (C (string2Name q)) [mkLam n t]
- quantP :: Parser String
- quantP = l $ choice [ (q:"") <$ choice (map char (q:a)) | (q,a) <- quantifiers ]
- atomP :: Parser Proposition
- atomP = choice
- [ l $ string "⊥" >> return (c "⊥")
- , l $ string "⊤" >> return (c "⊤")
- , l $ try (string "False") >> return (c "⊥")
- , l $ try (string "True") >> return (c "⊤")
- , do
- _ <- l $ char '¬' <|> char '~'
- p <- atomP
- return $ s "¬" [p]
- , do
- q <- quantP
- vname <- nameP
- _ <- l $ char '.'
- p <- termP
- return $ mkQuant q vname $ p
- , parenthesized termP
- , do
- head <- varOrConstP
- option head $ parenthesized $
- App head <$> termP `sepBy1` (l $ char ',')
- ]
- where
- c n = C (string2Name n)
- s n = App (c n)
- parenthesized = between (l $ char '(') (l $ char ')')
- varOrConstP :: Parser Term
- varOrConstP = choice
- [ do
- -- A hack for the test suite etc: prepending the name of a constant with V
- -- makes it a variable
- _ <- try (string "V ")
- num <- l $ option 0 (read <$> many1 digit)
- s <- l $ many1 alphaNum
- return $ V $ makeName s num
- , C <$> nameP
- ]
- nameP :: Rep a => Parser (Name a)
- nameP = l $ string2Name <$> many1 (alphaNum <|> char '_')