I was looking through Class::Phrasebook recently. Among its features is a miniature template system. You can give it data like this:

$phrase = 'Hello $dolly!'; $variables = { dolly => 'Nurse' };

...and that will be turned into "Hello Nurse!" The code to do this job looks like this:

$phrase =~ s/\$([a-zA-Z0-9_]+)/$variables->{$1}/g; # also process variables in $(var_name) format. $phrase =~ s/\$\(([a-zA-Z0-9_]+)\)/$variables->{$1}/g;

That's fine until someone does something like:

$phrase = '$foo $bar'; $variables = { foo => '$(bar)', bar => '$(foo)', };

When that happens, the first replacement above changes the phrase to "$(bar) $(foo)" (the correct result), and then the second replacement turns it into "$(foo) $(bar)" (wrong).

To be fair, this is a contrived example, and real world examples of this problem are few and far between. Nevertheless, when it does happen, it may be a real pain to debug.

It got me thinking about a bulletproof way to do this kind of interpolation, and I eventually came up with this:

my @phrase_parts = split /(\$(?:\(\w+\)|\w+))/, $phrase; foreach my $part ( @phrase_parts ) { $part =~ s{ \$ (\w+) }{$variables->{$1}}xms || $part =~ s{ \$ \( (\w+) \) }{$variables->{$1}}xms; } $phrase = join '', @phrase_parts;

I'm using the fact that split will include its delimiters in its result when the pattern you give it is wrapped in capturing parentheses. Everything that looks like a variable is an isolated element in @phrase_parts. Each one is subjected to replacement only once, so their replacements can't interfere with each other.

Now I'm wondering if there's an even better way. The only other thought I had was to use Template Toolkit, but that seemed like a much larger hammer than necessary. I'd be interested to hear thoughts about this from the monks.


In reply to How do I avoid double substitution when replacing many patterns? by kyle

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.