packages feed

katydid-0.3.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 qualified Ast
import Json (JsonTree, decodeJSON)
import Xml (decodeXML)
import qualified Parser

import qualified 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"
}