Example: IP addresses

Here’s a regular expression that checks if a string is a valid IPv4 or IPv6 address:

^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(:[0-9a-fA-F]{1,4}){1,6}|:(:[0-9a-fA-F]{1,4}){1,7}|::|fe80:(:[0-9a-fA-F]{1,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))$

Here’s the same regexp in free-spacing mode with some comments added:

(?x)
^(
    ( [0-9a-fA-F]{1,4} : ){7}       [0-9a-fA-F]{1,4}
  | ( [0-9a-fA-F]{1,4} : ){1,7}   :
  | ( [0-9a-fA-F]{1,4} : ){1,6}   : [0-9a-fA-F]{1,4}
  | ( [0-9a-fA-F]{1,4} : ){1,5} ( : [0-9a-fA-F]{1,4} ){1,2}
  | ( [0-9a-fA-F]{1,4} : ){1,4} ( : [0-9a-fA-F]{1,4} ){1,3}
  | ( [0-9a-fA-F]{1,4} : ){1,3} ( : [0-9a-fA-F]{1,4} ){1,4}
  | ( [0-9a-fA-F]{1,4} : ){1,2} ( : [0-9a-fA-F]{1,4} ){1,5}
  |   [0-9a-fA-F]{1,4} :        ( : [0-9a-fA-F]{1,4} ){1,6}
  |                    :        ( : [0-9a-fA-F]{1,4} ){1,7}
  | ::
  | fe80: ( : [0-9a-fA-F]{1,4} ){0,4} % [0-9a-zA-Z]+   # link-local IPv6 addresses with zone index
  | :: ( ffff ( : 0{1,4} )? : )?
    ( ( 25[0-5] | ( 2[0-4] | 1?[0-9] )? [0-9] ) \.){3}
      ( 25[0-5] | ( 2[0-4] | 1?[0-9] )? [0-9] )        # IPv4-mapped IPv6 addresses and IPv4-translated addresses
  | ( [0-9a-fA-F]{1,4} : ){1,4} :
    ( ( 25[0-5] | ( 2[0-4] | 1?[0-9] )? [0-9] ) \.){3}
      ( 25[0-5] | ( 2[0-4] | 1?[0-9] )? [0-9] )        # IPv4-Embedded IPv6 Address
  | ( ( 25[0-5] | ( 2[0-4] | 1?[0-9] )? [0-9] ) \.){3}
      ( 25[0-5] | ( 2[0-4] | 1?[0-9] )? [0-9] )        # IPv4
)$

And the equivalent Pomsky expression:

# segment in an IPv4 address
let num_v4 = range '0'-'255';

# segment in an IPv6 address
let num_v6 = [ascii_xdigit]{1,4};

# IPv4 address
let ipv4 = (num_v4 '.'){3} num_v4;

# link-local IPv6 address with zone index
# e.g. fe80::7:8%eth0   fe80::7:8%1
let link_local = 'fe80:' (':' num_v6){0,4} '%' [ascii_alnum]+;

# IPv4-Embedded IPv6 Address
# e.g. 2001:db8:3:4::192.0.2.33   64:ff9b::192.0.2.33
let ipv4_embedded = (num_v6 ':'){1,4} ':' ipv4;

# IPv4-mapped IPv6 address or IPv4-translated address
# e.g. ::255.255.255.255   ::ffff:255.255.255.255   ::ffff:0:255.255.255.255
let ipv4_mapped_translated = '::' ('ffff' (':' '0'{1,4})? ':')? ipv4;

# IPv6 address
let ipv6 = (
  | (num_v6 ':'){7}        num_v6
  | (num_v6 ':'){1,7}  ':'
  | (num_v6 ':'){1,6} (':' num_v6)
  | (num_v6 ':'){1,5} (':' num_v6){1,2}
  | (num_v6 ':'){1,4} (':' num_v6){1,3}
  | (num_v6 ':'){1,3} (':' num_v6){1,4}
  | (num_v6 ':'){1,2} (':' num_v6){1,5}
  | (num_v6 ':')      (':' num_v6){1,6}
  |         ':'       (':' num_v6){1,7}
  | '::'
  | link_local
  | ipv4_mapped_translated
  | ipv4_embedded
);

# IP address
^ (ipv6 | ipv4) $