logo       
Google Custom Search
    AddThis Social Bookmark Button
-->

RE: [SPOILER] Perl 'Expert' Quiz-of-the-Week #12: msg#00071

Subject: RE: [SPOILER] Perl 'Expert' Quiz-of-the-Week #12
Joseph F. Ryan:
# Is that quotemeta necessary?  I think that it would cause 
# problems since it would backslash inner '<', which would 
# prevent inner sequences from expanding.

It expands innermost-to-outermost, so inner sequences that were in the
original will be expanded beforehand.  If you're referring to inner
sequences added by the expansion function, I already mentioned that you
can just call the expand function for them manually.

What is an issue is that things can get quotemetaed two or more times:

    C:\>perl -e "print quotemeta quotemeta '< >'"
    \\\<\\\ \\\>
    C:\>perl -e "print quotemeta quotemeta '\< \>'"
    \\\\\\\<\\\ \\\\\\\>

The fix:

    sub unquotemeta(;$) {
        @_=($_) unless @_;
        local $_=shift;
        s/\\(.)/$1/gs;
        return $_;
    }
    
    sub default {
        warn "Invalid escape sequence used";
        return shift;
    }
    
    sub expand_escapes {
        my($str, $map)=@_;
        
        #We remove all the X<> sequences, inside-out and
        # left-to-right
        1 while $str =~ s{
            (\w) <
            (
                [^<>\\]*
                (?>
                    \\.
                    [^<>\\]*
                )*
            )
            >
        }
        {
            quotemeta &{$map->{$1}||\&default}(unquotemeta $2)
        }exs;
        
        return unquotemeta($str);
    }

MJD:  don't use my last solution--it's known bad.

# >I'm also working on a Perl 6 solution for this QOTW.  I'll 
# let you guys 
# >know how it turns out.  :^)
# >
# 
# Easy :)
# 
# 
#     sub expand_escapes($str,$map) {
#     ...

Yeah, but I want it to be all shiny.  :^)  Here's what I have so far:

    grammar EscapeExpander {
        has %.map is public;
        
        method new(; %map) {
            %.map=%map;
        }
    
        method expand(str $text) {
            return .string($text){text}
        }
        
        sub expand_escapes(str $text, %map) is exported('default') {
            return EscapeExpander.new(map => %map).expand($text);
        }
        
        rule string {
            $0 := ( <normal>* [ <escape> <self> ]? )
        }
        
        rule normal {
            $0 := \w ::: <-before <lt> >        #word chars that don't
start escapes
        |   <lt> ::: $text := <string> <gt>     #free angle brackets
                { let $0 := "<$text>" }
        |   \\ $0 := .                          #backslashed
        |   $0 := .                             #other normal characters
        }
    
        rule escape  {
            $char := \w <lt> $text := <string> <gt>
            {
                if(%.map{$char}) {
                    let $0 := %.map{$char}($text);
                }
                else {
                    warn "$char is not a valid escape sequence";
                    let $0 := $text;
                }
            }
        }
    }

The Big Question is whether you can create a grammar object the way I
have here.  I haven't been able to divine that from the relevant As/Es,
but I *think* I can.

--Brent Dax <brentdax-a09SyBuiYrA@xxxxxxxxxxxxxxxx>
@roles=map {"Parrot $_"} qw(embedding regexen Configure)

>How do you "test" this 'God' to "prove" it is who it says it is?
"If you're God, you know exactly what it would take to convince me. Do
that."
    --Marc Fleury on alt.atheism




<Prev in Thread] Current Thread [Next in Thread>