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 = ); chomp(my $replacement_pattern = ); 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

$body