use warnings; use strict; use 5.010; # required for Regexp::Grammars my $parser = do { use Regexp::Grammars; qr{ <[Block]>* \{ <[BlockItem]>* \} (?{ $MATCH = { $MATCH{BlockName} => $MATCH{BlockItem} } }) (?: | ) (?{ $MATCH = $MATCH{KeyValue} || $MATCH{Block} }) (?: | \[ <[Value=Word]>* % \s+ \] ) \; (?{ $MATCH = { $MATCH{Key} => $MATCH{Value} } }) [\w-]+ } }; my $input = do { open my $fh, '<', '1112435.txt' or die $!; local $/; <$fh> }; $input =~ $parser or die "Failed to parse input"; use Data::Dump 'pp'; say pp $/{Block}; #### [ { bob => [ { ed => [ { larry => [ { rule5 => [ { option => [{ "disable-server-response-inspection" => "no" }] }, { tag => ["some_tag"] }, { from => ["prod-L3"] }, { to => ["corp-L3"] }, { source => ["any"] }, { destination => ["any"] }, { "source-user" => ["any"] }, { category => ["any"] }, { application => ["any"] }, { service => ["any"] }, { "hip-profiles" => ["any"] }, { "log-start" => "no" }, { "log-end" => "yes" }, { "negate-source" => "no" }, { "negate-destination" => "no" }, { action => "allow" }, { "log-setting" => "orion_log" }, ], }, { rule6 => [ { option => [{ "disable-server-response-inspection" => "no" }] }, { tag => ["some_tag"] }, { from => ["prod-L3"] }, { to => ["corp-L3"] }, { source => ["any"] }, { destination => ["any"] }, { "source-user" => ["any"] }, { category => ["any"] }, { application => ["any"] }, { service => ["any"] }, { "hip-profiles" => ["any"] }, { "log-start" => "no" }, { "log-end" => "yes" }, { "negate-source" => "no" }, { "negate-destination" => "no" }, { action => "allow" }, { "log-setting" => "orion_log" }, ], }, ], }, ], }, ], }, ]