Perl 'Expert' Quiz-of-the-Week #12

Mark Jason Dominus <[email protected]> Wed, 12 Feb 2003 20:01:15 -0500
Newsgroups gmane.comp.lang.perl.qotw.quiz-of-the-week
Organization Plover Systems
Message-ID <[email protected]>
IMPORTANT: Please do not post solutions, hints, or other spoilers
        until at least 60 hours after the date of this message.
        Thanks.

IMPORTANTE: Por favor, no enviéis soluciones, pistas, o cualquier otra
        cosa que pueda echar a perder la resolución del problema hasta
        que hayan pasado por lo menos 60 horas desde el envío de este
        mensaje. Gracias.

IMPORTANT: S'il vous plaît, attendez au minimum 60 heures après la
        date de ce message avant de poster solutions, indices ou autres
        révélations. Merci.

WICHTIG: Bitte schicken Sie keine Lösungen, Tipps oder Hinweise für
        diese Aufgabe vor Ablauf von 60 Stunden nach dem Datum dieser
        Mail. Danke.

BELANGRIJK: Stuur aub geen oplossingen, hints of andere tips in de
        eerste 60 uur na het verzendingstijdstip van dit
        bericht. Waarvoor dank.

VNIMANIE: Pozhalujsta ne shlite reshenija, nameki na reshenija, i
        voobshe lyubye podskazki v techenie po krajnej mere 60 chasov
        ot daty etogo soobshenija.  Spasibo.

Qing3 Zhu4Yi4: Qing3 Ning2 Deng3Dao4 Jie1Dao4 Ben3 Xin4Xi2 Zhi1Hou4 60
        Xiao3Shi2, Zai4 Fa1Biao3 Jie3Da2, Ti2Shi4, Huo4 Qi2Ta1 Hui4
        Xie4Lou4 Da2An4 De5 Jian4Yi4.  Xie4Xie4.


----------------------------------------------------------------

Write a function, 'expand_escapes', which expands escape sequences in
text.  Escape sequences have the form

        X<.....>

where 'X' is any letter or digit and '.....' is any string that
contains balanced <...> sequences.  A backslash before a <, >, or \
removes its special meaning, so, for example,

        A<a b c D<edfg\>hij> k l m>

is a valid single escape sequence.  The arguments to 'expand_escapes'
are the string to be expanded, and a reference to a hash which maps
the escape codes to expansion functions; for example,

        expand_escapes("A<a b c D<edfg\>hij> k l m>",
                       { A => \&expand_a,
                         D => \&escape_sequence_d,
                       });
                
When 'expand_escapes' recognizes an escape sequence, say

        W<some text>

it should first expand escape sequences in 'some text', if there are
any, removing backslashes where appropriate; then look in the hash to
find the associated function for 'W'; invoke the function, passing the
with the expanded version of 'some text' as the argument, and finally,
replace the entire sequence 'W<...>' with the return value of the
function.  It should do this for every escape sequence in its string
argument.  The example above should return the value of:

        expand_a("a b c " . escape_sequence_d("edfg>hij") . " k l m");        

Another example:  given these arguments:

     expand_escapes("Result: R<12\>34>, S<Give me pie!>, R<abcS<te\<xt>xyz>",
                    { R => sub { scalar reverse $_[0] },
                      S => sub { join " ", split //, $_[0] }, 
                    }
                   );

'expand_escapes' should return the string

        "Result: 43>21, G i v e   m e   p i e !, zyxt x < e tcba"


The function should be efficient, even for large argument strings.