module Data.KeywordArgs.Parse (configParser) where import Data.Maybe (catMaybes) import Text.Parsec ((<|>), many, try, lookAhead, manyTill, char, anyChar, many1) import Text.Parsec.String (Parser) import Text.ParserCombinators.Parsec.Prim (GenParser) import Text.ParserCombinators.Parsec.Char (space, newline, oneOf, noneOf) import Control.Monad (liftM2) import Text.Parsec.Combinator (eof) import Control.Applicative ((<*), (*>), (<$>)) configParser :: Parser [(String, [String])] configParser = catMaybes <$> many lineWithArguments lineWithArguments :: Parser (Maybe (String, [String])) lineWithArguments = comment *> return Nothing <|> newline *> return Nothing <|> many1 (oneOf "\t ") *> return Nothing <|> Just <$> configurationOptionWithArguments configurationOptionWithArguments :: Parser (String, [String]) configurationOptionWithArguments = do _ <- many space keyword <- manyTill1 (noneOf "\n") keywordArgSeparator arguments <- argumentParser return (keyword, arguments) argumentParser :: Parser [String] argumentParser = manyTill1 (try quotedArgument <|> try unquotedArgument) (try endOfLineOrInput <|> try comment) quotedArgument :: Parser String quotedArgument = many (oneOf " \t") *> quote *> manyTill1 (noneOf "#") quote unquotedArgument :: Parser String unquotedArgument = many (oneOf " \t") *> many1 (noneOf "\" \t\n#") <* (try comment <|> try (oneOf " \t") *> return () <|> lookAhead (try endOfLineOrInput)) comment :: Parser () comment = try (many (oneOf " \t") *> char '#') *> manyTill anyChar endOfLineOrInput *> return () endOfLineOrInput :: Parser () endOfLineOrInput = newline *> return () <|> eof manyTill1 :: GenParser tok st a -> GenParser tok st end -> GenParser tok st [a] manyTill1 p end = liftM2 (:) p (manyTill p end) keywordArgSeparator :: Parser () keywordArgSeparator = many1 (oneOf "\t ") *> return () quote :: Parser Char quote = char '"'