packages feed

katydid-0.4.0.1: bench/Suite.hs

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

-- |
-- Suite parses the testsuite folder and creates benchmarks
module Suite (
    readBenches, BenchSuiteCase(..), stretch, runBench
) where

import qualified Data.Text as T (unpack)
import qualified Data.Text.IO as TIO (readFile)
import System.Directory (getCurrentDirectory, listDirectory, doesDirectoryExist)
import System.FilePath (FilePath, (</>), takeExtension, takeBaseName, takeDirectory)
import Text.XML.HXT.DOM.TypeDefs (XmlTree)
import Control.DeepSeq (NFData)
import GHC.Generics (Generic)
import Data.Int (Int64)

import Data.Katydid.Parser.Json (JsonTree, decodeJSON)
import Data.Katydid.Parser.Xml (decodeXML)

import qualified Data.Katydid.Relapse.Ast as Ast
import qualified Data.Katydid.Relapse.Parser as Parser
import qualified Data.Katydid.Relapse.Relapse as Relapse

runBench :: BenchSuiteCase -> IO Int
runBench (BenchSuiteCase _ g (XMLDatas inputs)) =
    return $ length $ Relapse.filter (compileGrammar g) inputs
runBench (BenchSuiteCase _ g (JsonDatas inputs)) =
    return $ length $ Relapse.filter (compileGrammar g) inputs

readBenches :: IO [BenchSuiteCase]
readBenches = do {
    path <- benchPath;
    exists <- doesDirectoryExist path;
    if exists
    then do {
        jsondirs <- ls $ path </> "json";
        -- TODO create xml benches in testsuite
        -- xmldirs <- ls $ path </> "xml";
        -- xmlBenches <- mapM readXMLBench xmldirs;
        jsonBenches <- mapM readJsonBench jsondirs;
        return $ filter (\(BenchSuiteCase _ g _) -> not (hasRecursion g)) jsonBenches
    } else return []
}

data BenchSuiteCase = BenchSuiteCase {
    benchname        :: String
    , grammar   :: String
    , input     :: EncodedData
} deriving (Show, Generic, NFData)

data EncodedData
    = XMLDatas [[XmlTree]]
    | JsonDatas [[JsonTree]]
    deriving (Show, Generic, NFData)

stretch :: BenchSuiteCase -> Int64 -> IO BenchSuiteCase
stretch (BenchSuiteCase name g (XMLDatas xs)) n = return $ BenchSuiteCase name g $ XMLDatas $ stretch' (fromIntegral n) xs
stretch (BenchSuiteCase name g (JsonDatas xs)) n = return $ BenchSuiteCase name g $ JsonDatas $ stretch' (fromIntegral n) xs

stretch' :: Int -> [a] -> [a]
stretch' n xs
    | length xs > n = take n xs
    | otherwise = xs ++ stretch' (n - length xs) xs

must :: Either String a -> a
must e = case e of
    (Left l) -> error l
    (Right r) -> r

getRelapse :: [FilePath] -> FilePath
getRelapse paths = head $ filter (\fname -> takeExtension fname == ".txt" && takeBaseName fname == "relapse") paths

filesWithExt :: String -> [FilePath] -> [FilePath]
filesWithExt ext = filter (\fname -> takeExtension fname == ext && takeBaseName fname /= "relapse")

compileGrammar :: String -> Relapse.Grammar
compileGrammar s = case Relapse.parse s of
    (Left err) -> error $ "given input: <" ++ s ++ "> got compile error: " ++ show err
    (Right r) -> r

hasRecursion :: String -> Bool
hasRecursion s = case Parser.parseGrammar s >>= Ast.hasRecursion of
    (Left err) -> error $ "given input: <" ++ s ++ "> got parse error: " ++ show err
    (Right r) -> r

readFileStrict :: FilePath -> IO String
readFileStrict = fmap T.unpack . TIO.readFile

readJsonBench :: FilePath -> IO BenchSuiteCase
readJsonBench path = do {
    files <- ls path;
    grammarData <- readFileStrict $ getRelapse files;
    jsonDatas <- mapM readFileStrict $ filesWithExt ".json" files;
    return $ BenchSuiteCase
        (takeBaseName path ++ "Json")
        grammarData
        (JsonDatas $ map (must . decodeJSON) jsonDatas)
}

readXMLBench :: FilePath -> IO BenchSuiteCase
readXMLBench path = do {
    files <- ls path;
    grammarData <- readFileStrict $ getRelapse files;
    xmlDatas <- mapM readFileStrict $ filesWithExt ".xml" files;
    return $ BenchSuiteCase
        (takeBaseName path ++ "XML")
        grammarData
        (XMLDatas $ map decodeXML xmlDatas)
}

ls :: FilePath -> IO [FilePath]
ls path = do {
    dirs <- listDirectory path;
    return $ map (path </>) dirs
}

benchPath :: IO FilePath
benchPath = do {
     path <- getCurrentDirectory;
     return $ takeDirectory path </> "testsuite" </> "relapse" </> "benches"
}