-- | Consult the TPTP World web services.
{-# OPTIONS_GHC -fno-warn-incomplete-record-updates #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UnicodeSyntax #-}
module OnlineATPs.Consult
( getOnlineATPs
, getResponseSystemOnTPTP
, getSystemATP
, getSystemATPWith
, getSystemOnTPTP
, Msg
) where
#if __GLASGOW_HASKELL__ <= 708
import Control.Applicative ( (<$>) )
#endif
import Control.Arrow ( (***) )
#if __GLASGOW_HASKELL__ <= 710
import Control.Monad.Reader ( MonadIO ( liftIO ) )
#else
import Control.Monad.IO.Class ( MonadIO ( liftIO ) )
#endif
import qualified Data.ByteString.Lazy as L
import qualified Data.HashMap.Strict as HashMap
import Data.ByteString.Internal ( packChars )
import Data.Char ( toLower )
import Data.List ( isPrefixOf )
import Data.List.Split ( splitOn )
import Data.Maybe ( fromJust, isNothing )
import Network ( withSocketsDo )
import Network.HTTP ( getRequest, getResponseBody, simpleHTTP )
import Network.HTTP.Client
( defaultManagerSettings
, httpLbs
, newManager
, parseRequest
, responseBody
, urlEncodedBody
)
import OnlineATPs.Defaults ( getDefaults )
import OnlineATPs.Options ( getManageOpt, Options (..) )
import OnlineATPs.SystemATP ( SystemATP (..), isFOFATP, setTimeLimit )
import OnlineATPs.SystemOnTPTP
( SystemOnTPTP (..)
, getDataSystemOnTPTP
, setFORMULAEProblem
, setSystems
)
import OnlineATPs.Urls ( urlSystemOnTPTP, urlSystemOnTPTPReply )
import Text.HTML.TagSoup
-- | Informative Message.
type Msg = String
getNameTag ∷ Tag String → String
getNameTag = fromAttrib "name"
prefixSystem ∷ Tag String → Bool
prefixSystem tag = isPrefixOf "System___" $ getNameTag tag
prefixTimeLimit ∷ Tag String → Bool
prefixTimeLimit tag = isPrefixOf "TimeLimit___" $ getNameTag tag
prefixTransform ∷ Tag String → Bool
prefixTransform tag = isPrefixOf "Transform___" $ getNameTag tag
prefixFormat ∷ Tag String → Bool
prefixFormat tag = isPrefixOf "Format___" $ getNameTag tag
prefixCommand ∷ Tag String → Bool
prefixCommand tag = isPrefixOf "Command___" $ getNameTag tag
matchSystem ∷ Tag String → Bool
matchSystem t = (t ~== "") && prefixSystem t
matchTimeLimit ∷ Tag String → Bool
matchTimeLimit t = (t ~=="") && prefixTimeLimit t
matchTransform ∷ Tag String → Bool
matchTransform t = (t ~=="") && prefixTransform t
matchFormat ∷ Tag String → Bool
matchFormat t = (t ~=="") && prefixFormat t
matchCommand ∷ Tag String → Bool
matchCommand t = (t ~=="") && prefixCommand t
matchApplication ∷ Tag String → Bool
matchApplication = (~=="")
matchCellATP ∷ [Tag String → Bool]
matchCellATP = [
matchSystem
, matchTimeLimit
, matchTransform
, matchFormat
, matchCommand
]
isInfoATP ∷ Tag String → Bool
isInfoATP t = isTagOpen t && any ($ t) matchCellATP
getInfoATP ∷ [Tag String] → [Tag String]
getInfoATP tags = getTags tags False
where
getTags ∷ [Tag String] → Bool → [Tag String]
getTags [] _ = []
getTags (t:ts) False
| isTagOpen t && matchApplication t = getTags ts True
| isInfoATP t = t : getTags ts False
| otherwise = getTags ts False
getTags (t:ts) True = t : getTags ts False
openURL ∷ String → IO String
openURL x = getResponseBody =<< simpleHTTP (getRequest x)
chucksOfSix ∷ [Tag String] → [[Tag String]]
chucksOfSix [] = []
chucksOfSix xs = take 6 xs : chucksOfSix (drop 6 xs)
renameATPs ∷ [SystemATP] → [SystemATP]
renameATPs [] = []
renameATPs atps = putVer atps 2
where
putVer ∷ [SystemATP] → Int → [SystemATP]
putVer [] _ = []
putVer [x] _ = [x]
putVer (x:y:ys) v
| sysName x == sysName y = x: y { sysKey = sysKey y ++ show v} : putVer ys (v+1)
| otherwise = x: y : putVer ys 2
getVal ∷ Tag String → String
getVal = fromAttrib "value"
tagsToSystemATP ∷ [Tag String] → SystemATP
tagsToSystemATP [tSys, tTime, tTrans, tFormat, tCmd, tApp] = newATP
where
info ∷ [String]
info = splitOn "---" $ getVal tSys
name, version ∷ String
name = head info
version = last info
newATP ∷ SystemATP
newATP = SystemATP
{ sysName = name
, sysKey = "online-" ++ map toLower name
, sysVersion = version
, sysTimeLimit = getVal tTime
, sysFormat = getVal tFormat
, sysTransform = getVal tTrans
, sysCommand = getVal tCmd
, sysApplication = fromTagText tApp
}
tagsToSystemATP _ = NoSystemATP
-- | The function 'getOnlineATPs' returns a list of ATPs given some options
-- using 'Options' type, and return the list using the 'SystemATP' type.
getOnlineATPs ∷ Options → IO [SystemATP]
getOnlineATPs opts = do
tags ← canonicalizeTags . parseTags <$> openURL urlSystemOnTPTP
let systems ∷ [SystemATP]
systems = renameATPs $ map tagsToSystemATP $ chucksOfSix $ getInfoATP tags
if optFOF opts
then return $ filter isFOFATP systems
else return systems
-- | The function 'getSystemATPWith' choose from a list the ATP most accurate
-- according to the name given in the second input. If there is not such a ATP,
-- this method returns 'NoSystemATP'.
getSystemATPWith ∷ [SystemATP] → String → SystemATP
getSystemATPWith _ "" = NoSystemATP
getSystemATPWith atps name =
if not $ "online-" `isPrefixOf` name then
getSystemATPWith atps $ "online-" ++ name
else
case lookup name (zip (map sysKey atps) atps) of
Just atp → atp
_ → NoSystemATP
-- | The function 'getSystemATP' tries to find an ATP given the specification -- from its input.
getSystemATP ∷ Options → IO SystemATP
getSystemATP opts =
let name = optVersionATP opts in
if | null name → return NoSystemATP
| not $ "online-" `isPrefixOf` name →
getSystemATP $ opts { optVersionATP = "online-" ++ name }
| otherwise → do
atps ∷ [SystemATP] ← getOnlineATPs opts
let namesATPs ∷ [String]
namesATPs = map sysKey atps
let mapATP = HashMap.fromList $ zip namesATPs atps
-- Future:
-- The idea is when the name is not valid, we'll try to find
-- the most similar ATP. We can do this using Levenstein
-- The HashMap is not necessary yet. Anyway, I'll use it.
return $ HashMap.lookupDefault NoSystemATP name mapATP
-- | The function 'getResponseSystemOnTPTP' performs a request to the TPTP
-- World based on the form information of the input 'SystemOnTPTP'.
getResponseSystemOnTPTP ∷ SystemOnTPTP → IO L.ByteString
getResponseSystemOnTPTP spec = withSocketsDo $ do
initReq ← parseRequest urlSystemOnTPTPReply
let dataForm ∷ [(String, String)]
dataForm = getDataSystemOnTPTP spec
let form = map (packChars *** packChars) dataForm
let request = urlEncodedBody form initReq
manager ← newManager defaultManagerSettings
res ← httpLbs request manager
liftIO $ do
let response = responseBody res
return response
-- | The function 'getSystemOnTPTP' reads some options including the problem file and it sends all this information to TPTP World.
getSystemOnTPTP ∷ Options → IO (Either Msg SystemOnTPTP)
getSystemOnTPTP opts = do
atps ∷ [SystemATP] ← getOnlineATPs opts
let listATPs ∷ [SystemATP]
listATPs =
if optWithAll opts
then atps
else map (getSystemATPWith atps) (getManageOpt (optATP opts))
let time ∷ String
time = show $ optTime opts
let setATPs ∷ [SystemATP]
setATPs = map (`setTimeLimit` time) listATPs
defaults ∷ SystemOnTPTP ← getDefaults
let file ∷ Maybe FilePath
file = optInputFile opts
if isNothing file
then return $ Left "Missing input file"
else do
contentFile ∷ String ← readFile $ fromJust file
let form ∷ SystemOnTPTP
form = setFORMULAEProblem (setSystems defaults setATPs) contentFile
return $ Right form