craft-0.0.0.1: src/Craft/Hosts/Parser.hs
module Craft.Hosts.Parser where
-- |This file is just to keep Megaparsec from conflicting with other modules
import Control.Monad (void)
import Data.List (intercalate)
import Text.Megaparsec
import Text.Megaparsec.String
import Craft.Hosts.Types
import Craft.Internal.Helpers.Parsing
import Craft.Types
parseLine :: Int -> String -> Craft (Maybe (IP, [Name]))
parseLine ln s =
case runParser lineParser (fromAbsFile hostsfp) s of
Right x -> return x
Left err -> $craftError $ show err
where
lineParser :: Parser (Maybe (IP, [Name]))
lineParser = do
pos <- getPosition
setPosition $ pos {sourceLine = ln}
space
try (comment >> return Nothing) <|> try (Just <$> item)
<|> (end >> return Nothing)
comment :: Parser ()
comment = label "comment" $ do
void $ char '#'
void $ manyTill (noneOf "\r\n") end
return ()
item :: Parser (IP, [Name])
item = do
ip <- try ipv4 <|> ipv6
skipSome white
name <- hostname
skipMany white
as <- aliases
void $ optional comment
return (ip, name:as)
aliases :: Parser [Name]
aliases = label "aliases" $ hostname `sepEndBy` many white
num :: Parser String
num = choice [ string "0", nonzero ]
nonzero :: Parser String
nonzero = do
a <- oneOf ['1'..'9'] <?> "non-zero digit with non-zero part"
rest <- count' 0 2 digitChar
if (read (a:rest) :: Int) > 255
then fail "IP address parts must be 0 <= x <= 255"
else return (a:rest)
hostname :: Parser Name
hostname = Name <$> some (alphaNumChar <|> oneOf ".-")
dot :: Parser ()
dot = void $ char '.'
ipv4 :: Parser IP
ipv4 = label "ipv4 address" $ do
p1 <- nonzero
dot
p2 <- num
dot
p3 <- num
dot
p4 <- num
notFollowedBy digitChar
return . IP $ intercalate "." [p1, p2, p3, p4]
ipv6 :: Parser IP
ipv6 = IP <$> some (hexDigitChar <|> char ':') <?> "ipv6 address"
white :: Parser ()
white = void $ (char ' ' <?> "space character") <|> tab