add dgl to contributors
[p5sagit/Eval-WithLexicals.git] / lib / Eval / WithLexicals.pm
index 1517ef7..bd70d69 100644 (file)
@@ -3,6 +3,9 @@ package Eval::WithLexicals;
 use Moo;
 use Sub::Quote;
 
+our $VERSION = '1.000000'; # 1.0.0
+$VERSION = eval $VERSION;
+
 has lexicals => (is => 'rw', default => quote_sub q{ {} });
 
 {
@@ -33,13 +36,14 @@ sub eval {
 ${setup}
 sub Eval::WithLexicals::Cage::current_line {
 package ${package};
+#line 1 "(eval)"
 ${to_eval}
 ;sub Eval::WithLexicals::Cage::pad_capture { }
 BEGIN { Eval::WithLexicals::Util::capture_list() }
 sub Eval::WithLexicals::Cage::grab_captures {
-  no warnings 'closure'; no strict 'refs';
-  package Eval::WithLexicals::Cage;!;
-  $self->_eval_do(\$current_code, $self->lexicals);
+  no warnings 'closure'; no strict 'vars';
+  package Eval::WithLexicals::VarScope;!;
+  $self->_eval_do(\$current_code, $self->lexicals, $to_eval);
   my @ret;
   my $ctx = $self->context;
   if ($ctx eq 'list') {
@@ -51,13 +55,26 @@ sub Eval::WithLexicals::Cage::grab_captures {
   }
   $self->lexicals({
     %{$self->lexicals},
-    %{Eval::WithLexicals::Cage::grab_captures()}
+    %{$self->_grab_captures},
   });
   @ret;
 }
 
+sub _grab_captures {
+  my ($self) = @_;
+  my $cap = Eval::WithLexicals::Cage::grab_captures();
+  foreach my $key (keys %$cap) {
+    my ($sigil, $name) = $key =~ /^(.)(.+)$/;
+    my $var_scope_name = $sigil.'Eval::WithLexicals::VarScope::'.$name;
+    if ($cap->{$key} eq eval "\\${var_scope_name}") {
+      delete $cap->{$key};
+    }
+  }
+  $cap;
+}
+
 sub _eval_do {
-  my ($self, $text_ref) = @_;
+  my ($self, $text_ref, $lexicals, $original) = @_;
   local @INC = (sub {
     if ($_[1] eq '/eval_do') {
       open my $fh, '<', $text_ref;
@@ -66,7 +83,7 @@ sub _eval_do {
       ();
     }
   }, @INC);
-  do '/eval_do' or die "Error: $@\nCompiling: $$text_ref";
+  do '/eval_do' or die $@;
 }
 
 {
@@ -76,7 +93,7 @@ sub _eval_do {
 
   sub capture_list {
     my $pad_capture = \&Eval::WithLexicals::Cage::pad_capture;
-    my @names = map $_->PV, grep $_->isa('B::PV'),
+    my @names = grep $_ ne '&', map $_->PV, grep $_->isa('B::PV'),
       svref_2object($pad_capture)->OUTSIDE->PADLIST->ARRAYelt(0)->ARRAY;
     $Eval::WithLexicals::current_code .=
       '+{ '.join(', ', map "'$_' => \\$_", @names).' };'
@@ -84,4 +101,102 @@ sub _eval_do {
   }
 }
 
+=head1 NAME
+
+Eval::WithLexicals - pure perl eval with persistent lexical variables
+
+=head1 SYNOPSIS
+
+  # file: bin/tinyrepl
+
+  #!/usr/bin/env perl
+
+  use strictures 1;
+  use Eval::WithLexicals;
+  use Term::ReadLine;
+  use Data::Dumper;
+
+  $SIG{INT} = sub { warn "SIGINT\n" };
+
+  { package Data::Dumper; no strict 'vars';
+    $Terse = $Indent = $Useqq = $Deparse = $Sortkeys = 1;
+    $Quotekeys = 0;
+  }
+
+  my $eval = Eval::WithLexicals->new;
+  my $read = Term::ReadLine->new('Perl REPL');
+  while (1) {
+    my $line = $read->readline('re.pl$ ');
+    exit unless defined $line;
+    my @ret; eval {
+      local $SIG{INT} = sub { die "Caught SIGINT" };
+      @ret = $eval->eval($line); 1;
+    } or @ret = ("Error!", $@);
+    print Dumper @ret;
+  }
+
+  # shell session:
+
+  $ perl -Ilib bin/tinyrepl 
+  re.pl$ my $x = 0;
+  0
+  re.pl$ ++$x;
+  1
+  re.pl$ $x + 3;
+  4
+  re.pl$ ^D
+  $
+
+=head1 METHODS
+
+=head2 new
+
+  my $eval = Eval::WithLexicals->new(
+    lexicals => { '$x' => \1 },      # default {}
+    in_package => 'PackageToEvalIn', # default Eval::WithLexicals::Scratchpad
+    context => 'scalar',             # default 'list'
+  );
+
+=head2 eval
+
+  my @return_value = $eval->eval($code_to_eval);
+
+=head2 lexicals
+
+  my $current_lexicals = $eval->lexicals;
+
+  $eval->lexicals(\%new_lexicals);
+
+=head2 in_package
+
+  my $current_package = $eval->in_package;
+
+  $eval->in_package($new_package);
+
+=head2 context
+
+  my $current_context = $eval->context;
+
+  $eval->context($new_context); # 'list', 'scalar' or 'void'
+
+=head1 AUTHOR
+
+Matt S. Trout <mst@shadowcat.co.uk>
+
+=head1 CONTRIBUTORS
+
+David Leadbeater <dgl@dgl.cx>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2010 the Eval::WithLexicals L</AUTHOR> and L</CONTRIBUTORS>
+as listed above.
+
+=head1 LICENSE
+
+This library is free software and may be distributed under the same terms
+as perl itself.
+
+=cut
+
 1;