-- boilerplate {{{
{-# LANGUAGE TupleSections #-}
module Version (CurrentFormat, parseCurrentFormat, pprintCurrentFormat, versionNumber, versionYeganesh, versionStrip) where
import Control.Monad (liftM2)
import Data.Char (isSpace)
import Data.Map (Map, assocs, elems, empty, fromList)
import Data.Time (UTCTime, getCurrentTime)
import Data.Version (showVersion)
import Lex (ReadP, (+++), char, lexDecNumber, lexString, readP_to_S, readS_to_P, sepBy, string)
import Paths_yeganesh (version)
type CurrentFormat = FormatV4
versionNumber :: String
versionYeganesh :: String
versionStrip :: String
pprintCurrentFormat :: CurrentFormat -> String
parseCurrentFormat :: String -> IO CurrentFormat
versionNumber = showVersion version
versionYeganesh = "yeganesh version " ++ versionNumber
versionStrip = "yeganesh-strip version " ++ versionNumber
pprintCurrentFormat = pprintFormatV4
parseCurrentFormat = parseFormatV4
-- }}}
-- v4 {{{
type FormatV4 = FormatV3
pprintFormatV4 :: FormatV4 -> String
pprintFormatV4 (now, cv) = unlines $
show now :
[show n ++ " " ++ line | (line,n) <- assocs cv]
parseFormatV4 :: String -> IO FormatV4
parseFormatV4 = parse v4 parseFormatV3 return
v4 :: String -> Maybe FormatV4
v4 = parseLines . lines where
parseLines [] = Nothing
parseLines (h:m) = liftM2 combine (parseHeader h) (mapM parseLine m)
combine h m = (h, fromList [(b,a) | (a,b) <- m])
parseHeader = readMaybe readp
parseLine s = case break isSpace s of
(n, ' ':line) -> fmap (,line) (readMaybe lexDouble n)
_ -> Nothing
-- }}}
-- v3 {{{
type FormatV3 = FormatV2
-- be stricter about what you accept, but faster at parsing
parseFormatV3 :: String -> IO FormatV3
parseFormatV3 = parse (readMaybe v3) parseFormatV2 upgradeFormatV2
v3 :: ReadP FormatV3
v3 = lexTuple readp (lexMap lexString lexDouble) where
lexTuple lexA lexB = do
_ <- char '('
a <- lexA
_ <- char ','
b <- lexB
_ <- char ')'
return (a,b)
lexList lexElem = do
_ <- char '['
vs <- sepBy lexElem (char ',') +++ return []
_ <- char ']'
return vs
lexMap lexKey lexValue = do
_ <- string "fromList "
fmap fromList (lexList (lexTuple lexKey lexValue))
-- }}}
-- v2 {{{
type FormatV2 = (UTCTime, FormatV1)
upgradeFormatV2 :: FormatV2 -> IO FormatV3
upgradeFormatV2 = return
parseFormatV2 :: String -> IO FormatV2
parseFormatV2 = parse (readMaybe readp) parseFormatV1 upgradeFormatV1
-- }}}
-- v1 {{{
type FormatV1 = Map String Double
upgradeFormatV1 :: FormatV1 -> IO FormatV2
upgradeFormatV1 v1 = fmap (flip (,) popularities) getCurrentTime where
maximal = case (elems v1, maximum (elems v1)) of
([], _) -> 1
(_ , 0) -> 1
(_ , m) -> m
popularities = fmap (/maximal) v1
parseFormatV1 :: String -> IO FormatV1
parseFormatV1 = parse (readMaybe readp) (const (return empty)) return
-- }}}
-- utilities {{{
lexDouble :: ReadP Double
lexDouble = fmap fromRational lexDecNumber
parse :: (String -> Maybe new) -> (String -> IO old) -> (old -> IO new) -> (String -> IO new)
parse new old upgrade s = maybe (old s >>= upgrade) return (new s)
readMaybe :: ReadP a -> String -> Maybe a
readMaybe p s = case readP_to_S p s of
[(x, unparsed)] | all isSpace unparsed -> Just x
_ -> Nothing
readp :: Read a => ReadP a
readp = readS_to_P reads
-- }}}