Look at getChompedString comment:
Sometimes parsers like
int
orvariable
cannot do exactly what you need. The “chomping” family of functions is meant for that case!
So I would use getChompedString
, chompWhile
and andThen
to parse and validate each part:
type alias IPv4 =
{ a : Int
, b : Int
, c : Int
, d : Int
, mask : Int
}
segment : Parser Int
segment =
getChompedString (chompWhile Char.isDigit)
|> andThen checkSegment
checkSegment : String -> Parser Int
checkSegment str =
case String.toInt str of
Just n ->
if n >= 0 && n <= 255 then
succeed n
else
problem "invalid segment value"
Nothing ->
problem "segment is not a number"
mask : Parser Int
mask =
getChompedString (chompWhile Char.isDigit)
|> andThen checkMask
checkMask : String -> Parser Int
checkMask str =
case String.toInt str of
Just n ->
if n >= 0 && n <= 32 then
succeed n
else
problem "invalid subnet mask"
Nothing ->
problem "subnet mask is not a number"
ipv4 : Parser IPv4
ipv4 =
succeed IPv4
|= segment
|. symbol "."
|= segment
|. symbol "."
|= segment
|. symbol "."
|= segment
|. symbol "/"
|= mask
This way, you are also sure that a parsed IPv4 is valid:
https://ellie-app.com/4h5PrzTLJDBa1
You could also define an intRange
parser to factorize some code:
intRange : Int -> Int -> Parser Int
intRange from to =
getChompedString (chompWhile Char.isDigit)
|> andThen (checkRange from to)
checkRange : Int -> Int -> String -> Parser Int
checkRange from to str =
case String.toInt str of
Just n ->
if n >= from && n <= to then
succeed n
else
rangeProblem from to
Nothing ->
rangeProblem from to
rangeProblem : Int -> Int -> Parser a
rangeProblem from to =
problem <|
String.join " "
[ "expected a number between"
, String.fromInt from
, "and"
, String.fromInt to
]
ipv4 : Parser IPv4
ipv4 =
succeed IPv4
|= intRange 0 255
|. symbol "."
|= intRange 0 255
|. symbol "."
|= intRange 0 255
|. symbol "."
|= intRange 0 255
|. symbol "/"
|= intRange 0 32