in reply to Regexp::Common balanced curlies substitution

I would tend to approach the problem differently, factoring out regex elements instead of trying to sink them beneath multiple levels of interpolation, and using explicit functions to operate on extracted substrings. Although there's more code on a character-count basis, this likely makes maintenance easier six months (or two weeks) from now when someone -- maybe even you! -- tries to make sense of it all. Of course, there is still the problematic eval qq{ qq{$replacement_pattern} } expression to reckon with, but you can't suck all the fun out of life.

Note that the placeholder strings in the replacement pattern specifier taken from the file are changed to $tag and $body from the, to my mind, more fragile $2 and $1.

Getting rid of $inpattern and inlining its pattern elements in the substitution regex is, to me, desirable because it puts regex captures 'closer' to the point at which they are used. (The example code works the same either way.)

BTW: This would probably be a nice place to use 5.10 named captures.


use warnings; use strict; use Regexp::Common; my $text = q{start \chapter{argument1}{argument2} end}; print "INPUT: '$text' \n"; # macro name and replacement pattern taken from a file, sans newlines. chomp(my $macro_name = <DATA>); chomp(my $replacement_pattern = <DATA>); my $parens = '{}'; my $arg = qr{ $RE{balanced}{-parens => $parens} }xms; my $intro = qr{ \\ }xms; my $inpattern = qr{ $intro $macro_name \s* ($arg) \s* ($arg) }xms; # print "inpattern $inpattern \n"; # FOR DEBUG $text =~ # s{ $inpattern } s{ $intro $macro_name \s* ($arg) \s* ($arg) } { my ($body, $tag) = ($1, $2); # nail down captures soonest validate_paired($parens, $body, $tag); trim_paired ($parens, $body, $tag); eval qq{ qq{$replacement_pattern} } or die "replacement failed: '$replacement_pattern'"; }xmsge; print "OUTPUT: '$text' \n"; print "\n"; sub trim_paired { my $pair = shift; # opening/closing pair of chars to trim my ($opener, $closer) = $pair =~ m{ \S }xmsg; die "pair of opening and closing chars not supplied: $pair" unless defined $opener and defined $closer; s{ \A $opener | $closer \z }''xmsg for @_; } sub validate_paired { my $pair = shift; # opening/closing pair of bracing chars my ($opener, $closer) = $pair =~ m{ \S }xmsg; die "pair of opening and closing chars not supplied" unless defined $opener and defined $closer; m{ \A $opener .* $closer \z }xms or die "not braced: '$_'" for @_; } __DATA__ chapter <h1 tag="$tag">$body</h1>

Output (both with and without $inpattern):

INPUT: 'start \chapter{argument1}{argument2} end' OUTPUT: 'start <h1 tag="argument2">argument1</h1> end'