{-# LANGUAGE OverloadedStrings #-} module Data.ConfigManager.Parser ( parseConfig ) where import Data.Text (Text) import qualified Data.Text as T import Data.Char (isSpace, isAlphaNum) import Text.ParserCombinators.Parsec import Data.ConfigManager.Types parseConfig :: Text -> Either Text [Expr] parseConfig input = case parse exprsParser "" (T.unpack (T.concat [input, "\n"])) of Right exprs -> Right exprs Left parserError -> Left . T.pack . show $ parserError exprsParser :: Parser [Expr] exprsParser = skip *> many (exprParser <* restOfLine <* skip) <* eof exprParser :: Parser Expr exprParser = (try bindingParser) <|> (try $ importParser "import" (Import Required)) <|> (try $ importParser "importMaybe" (Import Optional)) bindingParser :: Parser Expr bindingParser = do name <- nameParser _ <- spaces _ <- char '=' _ <- spaces value <- valueParser return $ Binding name value importParser :: String -> (FilePath -> Expr) -> Parser Expr importParser name exprFromPath = do _ <- string name _ <- spaces _ <- char '"' path <- many (noneOf "\"") _ <- char '"' return $ exprFromPath path nameParser :: Parser Name nameParser = do first <- letter rest <- many (satisfy (\c -> isAlphaNum c || c == '-' || c == '_')) return . T.pack $ first : rest valueParser :: Parser Value valueParser = T.strip . T.pack <$> many (noneOf "\n#") skip :: Parser () skip = (satisfy isSpace *> skip) <|> (comment *> skip) <|> (return ()) comment :: Parser () comment = do _ <- char '#' *> (many $ noneOf "\n") >> return () return () restOfLine :: Parser () restOfLine = do _ <- many (char ' ') _ <- optional comment _ <- newline return ()