You could just do it iteratively instead of recursively (also assuming this goes in the one-time quick-n-dirty clean-up category) .. i.e. find all the inner
<span ...>foo</span> instances (w/the help of a negative look-ahead--see
perlre) and replace them .. now repeat that until no more matches.
my $s = do {local $/=undef; <DATA>};
while( $s =~ s#<span style="(font-weight: bold;|font-style: italic;|te
+xt-decoration: underline;)">(?!.*?<span)(.*?)</span>#span2tag($1,$2)#
+sgei ){};
print $s;
sub span2tag {
my ($attr, $s) = @_;
return "<b>$s</b>" if $attr =~ /bold/;
return "<i>$s</i>" if $attr =~ /italic/;
return "<u>$s</u>" if $attr =~ /underline/;
return $s;
}
__DATA__
this <span style="font-weight: bold;">is</span> some
<span style="font-weight: bold;">test
<span style="font-style: italic;">text</span>
<span style="text-decoration: underline;">for</span> bolding</span>,
+ underlining and italicizing text.<br />
Update: Probably a little less efficient, but clearer code:
my $s = do {local $/=undef; <DATA>};
while(1){
my $matched = 0;
$matched ||= $s =~ s#<span style="font-weight: bold;">(?!.*?<span)(.
+*?)</span>#<b>$1</b>#sgi;
$matched ||= $s =~ s#<span style="font-style: italic;">(?!.*?<span)(
+.*?)</span>#<i>$1</i>#sgi;
$matched ||= $s =~ s#<span style="text-decoration: underline;">(?!.*
+?<span)(.*?)</span>#<u>$1</u>#sgi;
last unless $matched;
}