my $gr = (<<'__END_OF_GRAMMAR'); { use warnings; use strict; use Data::Dumper; sub get_duration { my %quantify = ( s => 1, m => 60, h => 3600, d => 86400, w => 604800, ); return $_[0] * $quantify{$_[1] || 's'}; } } host_str : /[a-z][0-9a-z\-]*/ domain_str : /(?: [a-z][0-9a-z\-]* \. )* [a-z][0-9a-z\-]* /x mac : /(?: [0-9a-f]{2} : ){5} [0-9a-f]{2} /ix ip : / (?: (?: 25[0-5] | 2[0-4][0-9] | [01]?[0-9]?[0-9] )\. ){3} (?: 25[0-5] | 2[0-4][0-9] | [01]?[0-9]?[0-9] ) /x filename : /"[^"\n\t\r]+"/ { substr ($item[1], 1, -1) } | /[^"\s]+/ ttl : 'ttl' duration { { ttl => $item[2] } } max_ttl : 'max-ttl' duration { { max_ttl => $item[2] } } duration : /(\d+)([smhdw]?)/ { $return = get_duration ($1, $2) } parse : directive(s) /\z/ {$item[1]} directive : server | keyfile | ttl | max_ttl | subnet(s) server : 'bind-server' ip { { bind_server => $item[2] } } keyfile : 'tsig-key' filename { { keyfile => $item[2] } } subnet : 'subnet' ip 'netmask' ip subnet_directive(s?) { { subnet => [ @item[2,4], @{$item[5]} ] } } subnet_directive: dns_update | domain | default | option | ttl | max_ttl | mapping dns_update : 'dns-update' { {dns_update => 1} } domain : 'domain' domain_str { {domain => $item[2]} } default : 'default' /[^\s;]+/ { {default => $item[2]} } option : 'option' /[^\n;]+/ #everything to the end of the line { {option => $item[2]} } mapping : ip identifier domain_str { $return = { $item[1] => { @{$item[2]}, domain_name => $item[3] } } } identifier : mac { ['mac', lc $item[1]] } | host_str { ['hostname', $item[1]] } __END_OF_GRAMMAR