module Language.Docker.Lexer where import Control.Monad (void) import Data.Char import Text.Parsec hiding (spaces) import Text.Parsec.Language (haskell) import Text.Parsec.String (Parser) import qualified Text.Parsec.Token as Token reserved :: String -> Parser () reserved name = void $ do _ <- try (caseInsensitiveString name) name spaces1 "at least one space after '" ++ name ++ "' followed by its arguments" natural :: Parser Integer natural = zeroNumber <|> Token.decimal haskell "positive number" where zeroNumber = char '0' >> return 0 commaSep :: Parser a -> Parser [a] commaSep p = sepBy p (symbol ",") stringLiteral :: Parser String stringLiteral = Token.stringLiteral haskell brackets :: Parser a -> Parser a brackets = between (symbol "[") (symbol "]") whiteSpace :: Parser () whiteSpace = void (char ' ' <|> char '\t') "space" space :: Parser () space = whiteSpace spaces1 :: Parser () spaces1 = void (many1 whiteSpace "at least one space") spaces :: Parser () spaces = void (many whiteSpace "spaces") symbol :: String -> Parser String symbol name = lexeme (string name) caseInsensitiveChar :: Char -> Parser Char caseInsensitiveChar c = char (toUpper c) <|> char (toLower c) caseInsensitiveString :: String -> Parser String caseInsensitiveString s = mapM caseInsensitiveChar s "\"" ++ s ++ "\"" charsWithEscapedSpaces :: String -> Parser String charsWithEscapedSpaces stopChars = do buf <- many1 $ noneOf ("\n\t\\ " ++ stopChars) try (jumpEscapeSequence buf) <|> try (backslashFollowedByChars buf) <|> return buf where backslashFollowedByChars buf = do backslashes <- many1 (char '\\') notFollowedBy (char ' ') rest <- charsWithEscapedSpaces stopChars return $ buf ++ backslashes ++ rest jumpEscapeSequence buf = do void $ string "\\ " rest <- charsWithEscapedSpaces stopChars return $ buf ++ ' ' : rest lexeme :: Parser a -> Parser a lexeme p = do x <- p spaces return x