I think you can combine the information from @-, @+ and $^N to get most of the way there:
sub last_closed {
my $last = $+[0];
my $len = length $^N;
for (1 .. $#+) {
return $_ if defined($+[$_])
&& $+[$_] == $last
&& $+[$_] == $len + $-[$_];
}
return undef;
}
"ad" =~ m{
(
(a) (?{ print last_closed() })
(b)? (?{ print last_closed() })
(c?) (?{ print last_closed() })
d
) (?{ print last_closed() })
}x;
This assumes a capture has just been closed; if that isn't necessarily the case, $last should instead be calculated as max(@+[1 .. $#+]).
Obviously this cannot report optional captures that did not capture; also, it can't tell the order of closing of empty strings - "c" =~ /((a*)b*)/ vs "c" =~ /(a*)(b*)/ - but if you are only interested in non-empty captures, I think this code will give the unique answer.
Hugo