bump version
[gitmo/Moo.git] / lib / Sub / Quote.pm
CommitLineData
a165a07f 1package Sub::Quote;
2
3use strictures 1;
4
5sub _clean_eval { eval $_[0] }
6
7use Sub::Defer;
8use B 'perlstring';
8a0ad775 9use Scalar::Util qw(weaken);
a165a07f 10use base qw(Exporter);
11
e1865995 12our $VERSION = '1.003001';
013a2be3 13$VERSION = eval $VERSION;
14
625d6219 15our @EXPORT = qw(quote_sub unquote_sub quoted_from_sub);
a165a07f 16
a165a07f 17our %QUOTED;
18
8a0ad775 19our %WEAK_REFS;
20
8c6626cf 21sub capture_unroll {
22 my ($from, $captures, $indent) = @_;
23 join(
24 '',
25 map {
26 /^([\@\%\$])/
17a8e3f0 27 or die "capture key should start with \@, \% or \$: $_";
8c6626cf 28 (' ' x $indent).qq{my ${_} = ${1}{${from}->{${\perlstring $_}}};\n};
29 } keys %$captures
30 );
31}
32
e57f338d 33sub inlinify {
34 my ($code, $args, $extra, $local) = @_;
35 my $do = 'do { '.($extra||'');
36 if (my ($code_args, $body) = $code =~ / +my \(([^)]+)\) = \@_;(.*)$/s) {
37 if ($code_args eq $args) {
38 $do.$body.' }'
39 } else {
f537c364 40 $do.'my ('.$code_args.') = ('.$args.'); '.$body.' }';
e57f338d 41 }
42 } else {
d89a7f56 43 my $assign = '';
44 if ($local || $args ne '@_') {
45 $assign = ($local ? 'local ' : '').'@_ = ('.$args.'); ';
46 }
47 $do.$assign.$code.' }';
e57f338d 48 }
49}
50
a165a07f 51sub quote_sub {
52 # HOLY DWIMMERY, BATMAN!
6f68f022 53 # $name => $code => \%captures => \%options
a165a07f 54 # $name => $code => \%captures
55 # $name => $code
6f68f022 56 # $code => \%captures => \%options
a165a07f 57 # $code
6f68f022 58 my $options =
59 (ref($_[-1]) eq 'HASH' and ref($_[-2]) eq 'HASH')
60 ? pop
61 : {};
a165a07f 62 my $captures = pop if ref($_[-1]) eq 'HASH';
625d6219 63 undef($captures) if $captures && !keys %$captures;
a165a07f 64 my $code = pop;
65 my $name = $_[0];
2b4634da 66 my $quoted_info;
67 my $deferred = defer_sub +($options->{no_install} ? undef : $name) => sub {
68 unquote_sub($quoted_info->[4]);
a165a07f 69 };
2b4634da 70 $quoted_info = [ $name, $code, $captures, undef, $deferred ];
71 weaken($QUOTED{$deferred} = $quoted_info);
a165a07f 72 return $deferred;
73}
74
75sub quoted_from_sub {
76 my ($sub) = @_;
2b4634da 77 $QUOTED{$sub||''};
a165a07f 78}
79
80sub unquote_sub {
81 my ($sub) = @_;
55d91f64 82 unless ($QUOTED{$sub}[3]) {
83 my ($name, $code, $captures) = @{$QUOTED{$sub}};
84
85 my $make_sub = "{\n";
86
efdff87e 87 my %captures = $captures ? %$captures : ();
88 $captures{'$_QUOTED'} = \$QUOTED{$sub};
89 $make_sub .= capture_unroll("\$_[1]", \%captures, 2);
55d91f64 90
55d91f64 91 $make_sub .= (
92 $name
93 # disable the 'variable $x will not stay shared' warning since
94 # we're not letting it escape from this scope anyway so there's
95 # nothing trying to share it
96 ? " no warnings 'closure';\n sub ${name} {\n"
efdff87e 97 : " \$_QUOTED->[3] = sub {\n"
55d91f64 98 );
99 $make_sub .= $code;
100 $make_sub .= " }".($name ? '' : ';')."\n";
101 if ($name) {
efdff87e 102 $make_sub .= " \$_QUOTED->[3] = \\&${name}\n";
55d91f64 103 }
104 $make_sub .= "}\n1;\n";
105 $ENV{SUB_QUOTE_DEBUG} && warn $make_sub;
106 {
107 local $@;
108 no strict 'refs';
109 local *{$name} if $name;
efdff87e 110 unless (_clean_eval $make_sub, \%captures) {
55d91f64 111 die "Eval went very, very wrong:\n\n${make_sub}\n\n$@";
112 }
113 }
114 }
a165a07f 115 $QUOTED{$sub}[3];
116}
117
efdff87e 118sub CLONE {
2b4634da 119 %QUOTED = map { defined $_ ? ($_->[4] => $_) : () } values %QUOTED;
120 weaken($_) for values %QUOTED;
efdff87e 121}
122
a165a07f 1231;
8595641b 124
0b6e5fff 125=head1 NAME
126
127Sub::Quote - efficient generation of subroutines via string eval
8595641b 128
129=head1 SYNOPSIS
130
131 package Silly;
132
133 use Sub::Quote qw(quote_sub unquote_sub quoted_from_sub);
134
135 quote_sub 'Silly::kitty', q{ print "meow" };
136
137 quote_sub 'Silly::doggy', q{ print "woof" };
138
6b3002b2 139 my $sound = 0;
8595641b 140
80c50620 141 quote_sub 'Silly::dagron',
6b3002b2 142 q{ print ++$sound % 2 ? 'burninate' : 'roar' },
8595641b 143 { '$sound' => \$sound };
144
145And elsewhere:
146
147 Silly->kitty; # meow
148 Silly->doggy; # woof
80c50620 149 Silly->dagron; # burninate
150 Silly->dagron; # roar
151 Silly->dagron; # burninate
8595641b 152
153=head1 DESCRIPTION
154
155This package provides performant ways to generate subroutines from strings.
156
157=head1 SUBROUTINES
158
159=head2 quote_sub
160
d925a566 161 my $coderef = quote_sub 'Foo::bar', q{ print $x++ . "\n" }, { '$x' => \0 };
8595641b 162
163Arguments: ?$name, $code, ?\%captures, ?\%options
164
165C<$name> is the subroutine where the coderef will be installed.
166
167C<$code> is a string that will be turned into code.
168
169C<\%captures> is a hashref of variables that will be made available to the
b4bd8687 170code. The keys should be the full name of the variable to be made available,
171including the sigil. The values should be references to the values. The
172variables will contain copies of the values. See the L</SYNOPSIS>'s
173C<Silly::dagron> for an example using captures.
8595641b 174
175=head3 options
176
177=over 2
178
179=item * no_install
180
181B<Boolean>. Set this option to not install the generated coderef into the
005c00f2 182passed subroutine name on undefer.
8595641b 183
184=back
185
186=head2 unquote_sub
187
005c00f2 188 my $coderef = unquote_sub $sub;
8595641b 189
b53b6c19 190Forcibly replace subroutine with actual code.
8595641b 191
005c00f2 192If $sub is not a quoted sub, this is a no-op.
193
8595641b 194=head2 quoted_from_sub
195
005c00f2 196 my $data = quoted_from_sub $sub;
197
198 my ($name, $code, $captures, $compiled_sub) = @$data;
199
200Returns original arguments to quote_sub, plus the compiled version if this
201sub has already been unquoted.
8595641b 202
005c00f2 203Note that $sub can be either the original quoted version or the compiled
204version for convenience.
8595641b 205
206=head2 inlinify
207
8a4aa214 208 my $prelude = capture_unroll '$captures', {
8595641b 209 '$x' => 1,
210 '$y' => 2,
211 };
212
213 my $inlined_code = inlinify q{
214 my ($x, $y) = @_;
215
216 print $x + $y . "\n";
217 }, '$x, $y', $prelude;
218
219Takes a string of code, a string of arguments, a string of code which acts as a
220"prelude", and a B<Boolean> representing whether or not to localize the
221arguments.
222
223=head2 capture_unroll
224
8a4aa214 225 my $prelude = capture_unroll '$captures', {
8595641b 226 '$x' => 1,
227 '$y' => 2,
8a4aa214 228 }, 4;
229
230Arguments: $from, \%captures, $indent
8595641b 231
232Generates a snippet of code which is suitable to be used as a prelude for
8a4aa214 233L</inlinify>. C<$from> is a string will be used as a hashref in the resulting
234code. The keys of C<%captures> are the names of the variables and the values
235are ignored. C<$indent> is the number of spaces to indent the result by.
9c820461 236
237=head1 CAVEATS
238
239Much of this is just string-based code-generation, and as a result, a few caveats
240apply.
241
242=head2 return
243
244Calling C<return> from a quote_sub'ed sub will not likely do what you intend.
245Instead of returning from the code you defined in C<quote_sub>, it will return
246from the overall function it is composited into.
247
248So when you pass in:
249
250 quote_sub q{ return 1 if $condition; $morecode }
251
252It might turn up in the intended context as follows:
253
254 sub foo {
255
256 <important code a>
257 do {
258 return 1 if $condition;
259 $morecode
260 };
261 <important code b>
262
263 }
264
265Which will obviously return from foo, when all you meant to do was return from
266the code context in quote_sub and proceed with running important code b.
072d158f 267
da2ddf62 268=head2 strictures
269
270Sub::Quote compiles quoted subs in an environment where C<< use strictures >>
271is in effect. L<strictures> enables L<strict> and FATAL L<warnings>.
272
273The following dies I<< Use of uninitialized value in print... >>
274
275 no warnings;
276 quote_sub 'Silly::kitty', q{ print undef };
277
278If you need to disable parts of strictures, do it within the quoted sub:
279
280 quote_sub 'Silly::kitty', q{ no warnings; print undef };
281
072d158f 282=head1 SUPPORT
283
1108b2e2 284See L<Moo> for support and contact information.
072d158f 285
286=head1 AUTHORS
287
288See L<Moo> for authors.
289
290=head1 COPYRIGHT AND LICENSE
291
292See L<Moo> for the copyright and license.