X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSub%2FQuote.pm;h=b5c5cc63120ca16711ec8032b7bfca53a353a91d;hb=7568ba55848e41dec5a2a6d04f9abab95dcb5fa3;hp=5c34715111ae502410ce70486fa8ccf28a0f9be5;hpb=f537c36467dafa0ff7eff17d822f4e04a6e11d2c;p=gitmo%2FMoo.git diff --git a/lib/Sub/Quote.pm b/lib/Sub/Quote.pm index 5c34715..b5c5cc6 100644 --- a/lib/Sub/Quote.pm +++ b/lib/Sub/Quote.pm @@ -6,6 +6,7 @@ sub _clean_eval { eval $_[0] } use Sub::Defer; use B 'perlstring'; +use Scalar::Util qw(weaken); use base qw(Exporter); our @EXPORT = qw(quote_sub unquote_sub quoted_from_sub); @@ -14,6 +15,8 @@ our %QUOTE_OUTSTANDING; our %QUOTED; +our %WEAK_REFS; + sub capture_unroll { my ($from, $captures, $indent) = @_; join( @@ -87,8 +90,11 @@ sub _unquote_all_outstanding { $ENV{SUB_QUOTE_DEBUG} && warn $assembled_code; } $assembled_code .= "\n1;"; - unless (_clean_eval $assembled_code, \@assembled_captures) { - die "Eval went very, very wrong:\n\n${debug_code}\n\n$@"; + { + local $@; + unless (_clean_eval $assembled_code, \@assembled_captures) { + die "Eval went very, very wrong:\n\n${debug_code}\n\n$@"; + } } $ENV{SUB_QUOTE_DEBUG} && warn $debug_code; %QUOTE_OUTSTANDING = (); @@ -117,12 +123,13 @@ sub quote_sub { $QUOTE_OUTSTANDING{$outstanding} = $QUOTED{$outstanding} = [ $name, $code, $captures ]; + weaken($WEAK_REFS{$outstanding} = $deferred); return $deferred; } sub quoted_from_sub { my ($sub) = @_; - $QUOTED{$sub||''}; + $WEAK_REFS{$sub||''} and $QUOTED{$sub||''}; } sub unquote_sub { @@ -132,3 +139,112 @@ sub unquote_sub { } 1; + +=head1 NAME + +Sub::Quote - efficient generation of subroutines via string eval + +=head1 SYNOPSIS + + package Silly; + + use Sub::Quote qw(quote_sub unquote_sub quoted_from_sub); + + quote_sub 'Silly::kitty', q{ print "meow" }; + + quote_sub 'Silly::doggy', q{ print "woof" }; + + my $sound = 0; + + quote_sub 'Silly::dagron', + q{ print ++$sound % 2 ? 'burninate' : 'roar' }, + { '$sound' => \$sound }; + +And elsewhere: + + Silly->kitty; # meow + Silly->doggy; # woof + Silly->dagron; # burninate + Silly->dagron; # roar + Silly->dagron; # burninate + +=head1 DESCRIPTION + +This package provides performant ways to generate subroutines from strings. + +=head1 SUBROUTINES + +=head2 quote_sub + + my $coderef = quote_sub 'Foo::bar', q{ print $x++ . "\n" }, { '$x' => \0 }; + +Arguments: ?$name, $code, ?\%captures, ?\%options + +C<$name> is the subroutine where the coderef will be installed. + +C<$code> is a string that will be turned into code. + +C<\%captures> is a hashref of variables that will be made available to the +code. See the L's C for an example using captures. + +=head3 options + +=over 2 + +=item * no_install + +B. Set this option to not install the generated coderef into the +passed subroutine name on undefer. + +=back + +=head2 unquote_sub + + my $coderef = unquote_sub $sub; + +Forcibly replace subroutine with actual code. Note that for performance +reasons all quoted subs declared so far will be globally unquoted/parsed in +a single eval. This means that if you have a syntax error in one of your +quoted subs you may find out when some other sub is unquoted. + +If $sub is not a quoted sub, this is a no-op. + +=head2 quoted_from_sub + + my $data = quoted_from_sub $sub; + + my ($name, $code, $captures, $compiled_sub) = @$data; + +Returns original arguments to quote_sub, plus the compiled version if this +sub has already been unquoted. + +Note that $sub can be either the original quoted version or the compiled +version for convenience. + +=head2 inlinify + + my $prelude = capture_unroll { + '$x' => 1, + '$y' => 2, + }; + + my $inlined_code = inlinify q{ + my ($x, $y) = @_; + + print $x + $y . "\n"; + }, '$x, $y', $prelude; + +Takes a string of code, a string of arguments, a string of code which acts as a +"prelude", and a B representing whether or not to localize the +arguments. + +=head2 capture_unroll + + my $prelude = capture_unroll { + '$x' => 1, + '$y' => 2, + }; + +Generates a snippet of code which is suitable to be used as a prelude for +L. The keys are the names of the variables and the values are (duh) +the values. Note that references work as values.