How to use elm/parser to parse Int's followed by a period (like in IP Addresses)?

Look at getChompedString comment:

Sometimes parsers like int or variable 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

https://ellie-app.com/4h5VXKJSyDpa1

14 Likes