hide some internal packages from PAUSE
[p5sagit/Eval-WithLexicals.git] / lib / Eval / WithLexicals.pm
CommitLineData
6f914695 1package Eval::WithLexicals;
2
3use Moo;
8d732f30 4use Moo::Role ();
6f914695 5use Sub::Quote;
6
fdcd0180 7our $VERSION = '1.003000'; # 1.3.0
8d080039 8$VERSION = eval $VERSION;
9
6f914695 10has lexicals => (is => 'rw', default => quote_sub q{ {} });
11
12{
13 my %valid_contexts = map +($_ => 1), qw(list scalar void);
14
15 has context => (
16 is => 'rw', default => quote_sub(q{ 'list' }),
17 isa => sub {
18 my ($val) = @_;
19 die "Invalid context type $val - should be list, scalar or void"
3092edd9 20 unless $valid_contexts{$val};
6f914695 21 },
22 );
23}
24
25has in_package => (
26 is => 'rw', default => quote_sub q{ 'Eval::WithLexicals::Scratchpad' }
27);
28
73a98f1c 29has prelude => (
30 is => 'rw', default => quote_sub q{ 'use strictures 1;' }
31);
32
8d732f30 33sub with_plugins {
34 my($class, @names) = @_;
35
36 Moo::Role->create_class_with_roles($class,
37 map "Eval::WithLexicals::With$_", @names);
38}
39
40sub setup_code {
41 my($self) = @_;
42 $self->prelude;
43}
44
45sub capture_code {
46 ( qq{ BEGIN { Eval::WithLexicals::Util::capture_list() } } )
47}
48
6f914695 49sub eval {
50 my ($self, $to_eval) = @_;
51 local *Eval::WithLexicals::Cage::current_line;
52 local *Eval::WithLexicals::Cage::pad_capture;
53 local *Eval::WithLexicals::Cage::grab_captures;
8d732f30 54
6f914695 55 my $package = $self->in_package;
8d732f30 56 my $setup_code = join '', $self->setup_code,
57 # $_[2] being what is passed to _eval_do below
58 Sub::Quote::capture_unroll('$_[2]', $self->lexicals, 2);
59
60 my $capture_code = join '', $self->capture_code;
61
62 local our $current_code = qq!
63${setup_code}
6f914695 64sub Eval::WithLexicals::Cage::current_line {
65package ${package};
e764ce9b 66#line 1 "(eval)"
6f914695 67${to_eval}
68;sub Eval::WithLexicals::Cage::pad_capture { }
8d732f30 69${capture_code}
6f914695 70sub Eval::WithLexicals::Cage::grab_captures {
40d8277f 71 no warnings 'closure'; no strict 'vars';
e8f16081 72 package! # hide from PAUSE
73 .q! Eval::WithLexicals::VarScope;!;
8d732f30 74 # rest is appended by Eval::WithLexicals::Util::capture_list, called
75 # during parsing by the BEGIN block from capture_code.
76
8d080039 77 $self->_eval_do(\$current_code, $self->lexicals, $to_eval);
8d732f30 78 $self->_run(\&Eval::WithLexicals::Cage::current_line);
79}
80
81sub _run {
82 my($self, $code) = @_;
83
6f914695 84 my @ret;
85 my $ctx = $self->context;
86 if ($ctx eq 'list') {
8d732f30 87 @ret = $code->();
6f914695 88 } elsif ($ctx eq 'scalar') {
8d732f30 89 $ret[0] = $code->();
6f914695 90 } else {
8d732f30 91 $code->();
6f914695 92 }
93 $self->lexicals({
94 %{$self->lexicals},
40d8277f 95 %{$self->_grab_captures},
6f914695 96 });
97 @ret;
98}
99
40d8277f 100sub _grab_captures {
101 my ($self) = @_;
102 my $cap = Eval::WithLexicals::Cage::grab_captures();
103 foreach my $key (keys %$cap) {
104 my ($sigil, $name) = $key =~ /^(.)(.+)$/;
105 my $var_scope_name = $sigil.'Eval::WithLexicals::VarScope::'.$name;
106 if ($cap->{$key} eq eval "\\${var_scope_name}") {
107 delete $cap->{$key};
108 }
109 }
110 $cap;
111}
112
6f914695 113sub _eval_do {
8d732f30 114 my ($self, $text_ref, $lexical, $original) = @_;
6f914695 115 local @INC = (sub {
116 if ($_[1] eq '/eval_do') {
117 open my $fh, '<', $text_ref;
118 $fh;
119 } else {
120 ();
121 }
122 }, @INC);
8d080039 123 do '/eval_do' or die $@;
6f914695 124}
125
126{
e8f16081 127 package # hide from PAUSE
128 Eval::WithLexicals::Util;
6f914695 129
130 use B qw(svref_2object);
131
132 sub capture_list {
133 my $pad_capture = \&Eval::WithLexicals::Cage::pad_capture;
4a3d69ab 134 my @names = grep $_ ne '&', map $_->PV, grep $_->isa('B::PV'),
6f914695 135 svref_2object($pad_capture)->OUTSIDE->PADLIST->ARRAYelt(0)->ARRAY;
136 $Eval::WithLexicals::current_code .=
137 '+{ '.join(', ', map "'$_' => \\$_", @names).' };'
138 ."\n}\n}\n1;\n";
139 }
140}
141
54153012 1421;
143__END__
144
8d080039 145=head1 NAME
146
147Eval::WithLexicals - pure perl eval with persistent lexical variables
148
149=head1 SYNOPSIS
150
151 # file: bin/tinyrepl
152
153 #!/usr/bin/env perl
154
155 use strictures 1;
156 use Eval::WithLexicals;
157 use Term::ReadLine;
158 use Data::Dumper;
8d732f30 159 use Getopt::Long;
160
161 GetOptions(
162 "plugin=s" => \my @plugins
163 );
8d080039 164
165 $SIG{INT} = sub { warn "SIGINT\n" };
166
167 { package Data::Dumper; no strict 'vars';
168 $Terse = $Indent = $Useqq = $Deparse = $Sortkeys = 1;
169 $Quotekeys = 0;
170 }
171
8d732f30 172 my $eval = @plugins
173 ? Eval::WithLexicals->with_plugins(@plugins)->new
174 : Eval::WithLexicals->new;
175
8d080039 176 my $read = Term::ReadLine->new('Perl REPL');
177 while (1) {
178 my $line = $read->readline('re.pl$ ');
179 exit unless defined $line;
180 my @ret; eval {
181 local $SIG{INT} = sub { die "Caught SIGINT" };
182 @ret = $eval->eval($line); 1;
183 } or @ret = ("Error!", $@);
184 print Dumper @ret;
185 }
186
187 # shell session:
188
189 $ perl -Ilib bin/tinyrepl
190 re.pl$ my $x = 0;
191 0
192 re.pl$ ++$x;
193 1
194 re.pl$ $x + 3;
195 4
196 re.pl$ ^D
197 $
198
199=head1 METHODS
200
201=head2 new
202
203 my $eval = Eval::WithLexicals->new(
204 lexicals => { '$x' => \1 }, # default {}
205 in_package => 'PackageToEvalIn', # default Eval::WithLexicals::Scratchpad
206 context => 'scalar', # default 'list'
73a98f1c 207 prelude => 'use warnings', # default 'use strictures 1'
8d080039 208 );
209
210=head2 eval
211
212 my @return_value = $eval->eval($code_to_eval);
213
214=head2 lexicals
215
216 my $current_lexicals = $eval->lexicals;
217
218 $eval->lexicals(\%new_lexicals);
219
220=head2 in_package
221
222 my $current_package = $eval->in_package;
223
224 $eval->in_package($new_package);
225
226=head2 context
227
228 my $current_context = $eval->context;
229
230 $eval->context($new_context); # 'list', 'scalar' or 'void'
231
73a98f1c 232=head2 prelude
233
234Code to run before evaling code. Loads L<strictures> by default.
235
236 my $current_prelude = $eval->prelude;
237
238 $eval->prelude(q{use warnings}); # only warnings, not strict.
239
8d732f30 240=head2 with_plugins
241
242 my $eval = Eval::WithLexicals->with_plugins("HintPersistence")->new;
243
244Construct a class with the given plugins. Plugins are roles located under
245a package name like C<Eval::WithLexicals::With*>.
246
247Current plugins are:
248
249=over 4
250
251=item * HintPersistence
252
253When enabled this will persist pragams and other compile hints between evals
254(for example the L<strict> and L<warnings> flags in effect). See
255L<Eval::WithLexicals::WithHintPersistence> for further details.
256
257=back
258
8d080039 259=head1 AUTHOR
260
261Matt S. Trout <mst@shadowcat.co.uk>
262
263=head1 CONTRIBUTORS
264
9a82ac00 265David Leadbeater <dgl@dgl.cx>
8d080039 266
f0d3f86b 267haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
268
8d080039 269=head1 COPYRIGHT
270
271Copyright (c) 2010 the Eval::WithLexicals L</AUTHOR> and L</CONTRIBUTORS>
272as listed above.
273
274=head1 LICENSE
275
276This library is free software and may be distributed under the same terms
277as perl itself.
278
279=cut