packages feed

ValveValueKeyvalue-1.0.0.0: app/Text/ValveVKV/Internal.hs

module Text.ValveVKV.Internal where
import Text.Parsec
import Text.Read (readMaybe)

alphabet :: [Char]
alphabet = "'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-+=.,#@!$%¨&*\\"

alphabetSpace :: [Char]
alphabetSpace = ' ':alphabet

data ValveKeyValueEntry
   = KVObject (Pair [ValveKeyValueEntry])
   | KVString (Pair String)
   | KVInt (Pair Int)
   deriving (Show)

liftVKV :: (String -> String) -> ValveKeyValueEntry -> ValveKeyValueEntry
liftVKV f (KVObject (Pair name s)) = KVObject (Pair (f name) s)
liftVKV f (KVInt (Pair name s)) = KVInt (Pair (f name) s)
liftVKV f (KVString (Pair name s)) = KVString (Pair (f name) s)


data Pair a = Pair String a
    deriving (Show)

unpair :: Pair a -> (String, a)
unpair (Pair str a) = (str,a)

entryParser :: Parsec String () ValveKeyValueEntry
entryParser =
    try objParse <|> try kvIntParse <|> try kvStringParse

whitespaceCommon :: Parsec String () Char
whitespaceCommon = choice $ fmap char "\t "

nameGrab :: Parsec String () String
nameGrab = many1 $ choice $ fmap char alphabet

nameGrabSpace :: Parsec String () String
nameGrabSpace = many1 $ choice $ fmap char alphabetSpace

objParse :: Parsec String () ValveKeyValueEntry
objParse = do
    many extendWhitespace
    many commentParser
    name <- nameGrab
    many (try extendWhitespace)
    char '{'
    many (try extendWhitespace)
    things <- vkvParser
    many (try extendWhitespace)
    char '}'
    many (try commentParser)
    many (try extendWhitespace)
    return $ KVObject $ Pair name things

extendWhitespace :: Parsec String () Char
extendWhitespace = choice $ try commentParser:fmap char "\t\n " :: Parsec String () Char

commentParser :: Parsec String () Char
commentParser = do
    many whitespaceCommon
    char '/'
    char '/'
    anyLoop
    where
        anyLoop :: Parsec String () Char
        anyLoop = do
            thisChar <- anyChar
            if thisChar == '\n' then
                return '\n'
            else do
                anyLoop
                return thisChar

kvStringParse :: Parsec String () ValveKeyValueEntry
kvStringParse = do
    many extendWhitespace
    many commentParser
    let stringGrab = many1 $ choice $ fmap char alphabet :: Parsec String () String
    let names = choice [nameGrab, between (char '"') (char '"') nameGrabSpace]
    many whitespaceCommon
    name <- names
    many1 whitespaceCommon
    value <- try (between (char '"') (char '"') nameGrabSpace) <|> try stringGrab
    many (try extendWhitespace)
    return $ KVString $ Pair name value

kvIntParse :: Parsec String () ValveKeyValueEntry
kvIntParse = do
    many extendWhitespace
    many commentParser
    let numberGrab = many1 $ choice $ fmap char "0123456789" :: Parsec String () String
    many (try whitespaceCommon)
    name <- nameGrab
    many1 whitespaceCommon
    valueRaw <- numberGrab
    many (try extendWhitespace)
    let value = readMaybe valueRaw :: Maybe Int
    case value of
        Just x -> return $ KVInt $ Pair name x
        Nothing -> fail ""

-- | The Parsec parser itself.
vkvParser :: Parsec String () [ValveKeyValueEntry]
vkvParser =
    many1 entryParser