my $grammar = <<'END_OF_GRAMMAR'; { use 5.012; #enable say() use Data::Dumper; } startrule: paragraph(s) paragraph: infobox { say Dumper(\@item); } | word(s) infobox: '{{Infobox' inner_block(s) '}}' { my $inner_blocks = join "", @{$item[2]}; $return = join "\n", $item[1], $inner_blocks, $item[4]; } inner_block: brace_block | word(s) { $return = join "\n", @{$item[1]} ; } brace_block: brace_block: lbrace(2..) { $lbraces = join '', @{$item[1]}; $rbraces = "}" x length $lbraces; } inner_block(s) "$rbraces" { #say Dumper(\@item); my $inner_blocks = join "", @{$item[3]}; $return = "$lbraces $inner_blocks $rbraces"; } word: m{ [^{}]+ }xms lbrace: / [{] /xms END_OF_GRAMMAR --output:-- $VAR1 = [ 'paragraph', '{{Infobox aaa bbb ccc {{ ddd eee fff ggg {{ hhh iii }}jjj }}{{{ kkk {{ lll }}mmm }}} ' ]; $VAR1 = [ 'paragraph', '{{Infobox aaa2 bbb2 ccc2 {{ ddd2 eee2 fff2 ggg2 {{ hhh2 iii2 }}jjj2 }}{{{ kkk2 {{ lll2 }}mmm2 }}} ' ]; $VAR1 = [ 'paragraph', '{{Infobox 111 ' ];