You can avoid the recursion by implementing your own looping:
my $p = Parse::RecDescent->new(<<'__END_OF_GRAMMAR__');
{
use strict;
use warnings;
}
parse : rec /\Z/ { $item[1] }
rec : POS_INT
{
$thisparser->_parserepeat(
$text,
\&ELEM, $item[1], $item[1], # ELEM(#)
$_noactions, $expectation, undef
)
}
{ [ $item[0] => $item[2] ] }
POS_INT : /\d+/
ELEM : /\S+/
__END_OF_GRAMMAR__
_parserepeat is the method called to handle rule(s), rule(s?), rule(4..6), etc.
If you don't want to break the box, you could break down the problem instead:
my $p = Parse::RecDescent->new(<<'__END_OF_GRAMMAR__');
{
use strict;
use warnings;
}
parse : rec /\Z/ { $item[1] }
rec : POS_INT rec_list[ $item[1] ] { [ $item[0] => $item[2] ] }
rec_list : { $arg[0] < 1 ? [] : undef }
| { $arg[0] < 10 ? 1 : undef }
ELEM rec_list[ $arg[0]-1 ]
{ [ $item[2], @{$item[3]} ] }
| { $arg[0] < 100 ? 1 : undef }
ELEM(10) rec_list[ $arg[0]-10 ]
{ [ @{$item[2]}, @{$item[3]} ] }
| { $arg[0] < 1000 ? 1 : undef }
ELEM(100) rec_list[ $arg[0]-100 ]
{ [ @{$item[2]}, @{$item[3]} ] }
| <error:Exceeded maximum list length of 999 elements>
POS_INT : /\d+/
ELEM : /\S+/
__END_OF_GRAMMAR__