user
The purpose of this task is to demonstrate parsing of text-format IP addresses, using IPv4 and IPv6.

Taking the following as inputs:

:   :   :   |  |  |
            | --- | --- |
            | 127.0.0.1 | The "localhost" IPv4 address |
            | 127.0.0.1:80 | The "localhost" IPv4 address, with a specified port (80) |
            | ::1 | The "localhost" IPv6 address |
            | [::1]:80 | The "localhost" IPv6 address, with a specified port (80) |
            | 2605:2700:0:3::4713:93e3 | Rosetta Code's primary server's public IPv6 address |
            | [2605:2700:0:3::4713:93e3]:80 | Rosetta Code's primary server's public IPv6 address, with a specified port (80) |

Task

Emit each described IP address as a hexadecimal integer representing the address, the address space, and the port number specified, if any.

In languages where variant result types are clumsy, the result should be ipv4 or ipv6 address number, something which says which address space was represented, port number and something that says if the port was specified.

Example

**127.0.0.1**   has the address number   **7F000001**   (2130706433 decimal)
in the ipv4 address space.

**::ffff:127.0.0.1**   represents the same address in the ipv6 address space where it has the
address number   **FFFF7F000001**   (281472812449793 decimal).

**::1**   has address number   **1**   and serves the same purpose in the ipv6 address
space that   **127.0.0.1**   serves in the ipv4 address space.
assistant
(formerly Perl 6)

```
grammar IP_Addr {
    token TOP { ^ [ <IPv4> | <IPv6> ] $ }
 
    token IPv4 {
        [ <d8> +% '.' ] <?{ $<d8> == 4 }> <port>?
                { @*by8 = @$<d8> }
    }

    token IPv6 {
        |     <ipv6>
        | '[' <ipv6> ']' <port>
    }

    token ipv6 {
        | <h16> +% ':' <?{ $<h16> == 8 }>
                { @*by16 = @$<h16> }
 
        | [ (<h16>) +% ':']? '::' [ (<h16>) +% ':' ]? <?{ @$0 + @$1 ≤ 8 }>
                { @*by16 = |@$0, |('0' xx 8 - (@$0 + @$1)), |@$1 }
 
        | '::ffff:' <IPv4>
                { @*by16 = |('0' xx 5), 'ffff', |(by8to16 @*by8) }
    }

    token d8  { (\d+) <?{ $0 < 256   }> }
    token d16 { (\d+) <?{ $0 < 65536 }> }
    token h16 { (<xdigit>+) <?{ $0.chars ≤ 4 }> }

    token port {
        ':' <d16> { $*port = +$<d16> }
    }
}

sub by8to16 (@m) { gather for @m -> $a,$b { take ($a * 256 + $b).fmt("%04x") } }

my @cases = <
    127.0.0.1
    127.0.0.1:80
    ::1
    [::1]:80
    2605:2700:0:3::4713:93e3
    [2605:2700:0:3::4713:93e3]:80
    2001:db8:85a3:0:0:8a2e:370:7334
    2001:db8:85a3::8a2e:370:7334
    [2001:db8:85a3:8d3:1319:8a2e:370:7348]:443
    192.168.0.1
    ::ffff:192.168.0.1
    ::ffff:71.19.147.227
    [::ffff:71.19.147.227]:80
    ::
    256.0.0.0
    g::1
    0000
    0000:0000
    0000:0000:0000:0000:0000:0000:0000:0000
    0000:0000:0000::0000:0000
    0000::0000::0000:0000
    ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
    ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff
    fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
    fff:ffff:0:ffff:ffff:ffff:ffff:ffff
>;

for @cases -> $addr {
    my @*by8;
    my @*by16;
    my $*port;

    IP_Addr.parse($addr);

    say $addr;
    if @*by16 {
        say "  IPv6: ", @*by16.map({:16(~$_)})».fmt("%04x").join;
        say "  Port: ", $*port if $*port;
    }
    elsif @*by8 {
        say "  IPv4: ", @*by8».fmt("%02x").join;
        say "  Port: ", $*port if $*port;
    }
    else {
        say "  BOGUS!";
    }
    say '';
}

```

Output:

```
127.0.0.1
  IPv4: 7f000001

127.0.0.1:80
  IPv4: 7f000001
  Port: 80

::1
  IPv6: 00000000000000000000000000000001

[::1]:80
  IPv6: 00000000000000000000000000000001
  Port: 80

2605:2700:0:3::4713:93e3
  IPv6: 260527000000000300000000471393e3

[2605:2700:0:3::4713:93e3]:80
  IPv6: 260527000000000300000000471393e3
  Port: 80

2001:db8:85a3:0:0:8a2e:370:7334
  IPv6: 20010db885a3000000008a2e03707334

2001:db8:85a3::8a2e:370:7334
  IPv6: 20010db885a3000000008a2e03707334

[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443
  IPv6: 20010db885a308d313198a2e03707348
  Port: 443

192.168.0.1
  IPv4: c0a80001

::ffff:192.168.0.1
  IPv6: 00000000000000000000ffffc0a80001

::ffff:71.19.147.227
  IPv6: 00000000000000000000ffff471393e3

[::ffff:71.19.147.227]:80
  IPv6: 00000000000000000000ffff471393e3
  Port: 80

::
  IPv6: 00000000000000000000000000000000

256.0.0.0
  BOGUS!

g::1
  BOGUS!

0000
  BOGUS!

0000:0000
  BOGUS!

0000:0000:0000:0000:0000:0000:0000:0000
  IPv6: 00000000000000000000000000000000

0000:0000:0000::0000:0000
  IPv6: 00000000000000000000000000000000

0000::0000::0000:0000
  IPv6: 00000000000000000000000000000000

ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
  IPv6: ffffffffffffffffffffffffffffffff

ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff
  BOGUS!

fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
  IPv6: 0fffffffffffffffffffffffffffffff

fff:ffff:0:ffff:ffff:ffff:ffff:ffff
  IPv6: 0fffffff0000ffffffffffffffffffff
```