Add persistent hints
[p5sagit/Eval-WithLexicals.git] / lib / Eval / WithLexicals.pm
index 1517ef7..7028702 100644 (file)
 package Eval::WithLexicals;
 
 use Moo;
-use Sub::Quote;
 
-has lexicals => (is => 'rw', default => quote_sub q{ {} });
+our $VERSION = '1.001000'; # 1.1.0
+$VERSION = eval $VERSION;
 
-{
-  my %valid_contexts = map +($_ => 1), qw(list scalar void);
+with 'Eval::WithLexicals::Role::Eval';
+with 'Eval::WithLexicals::Role::PreludeEachTime';
 
-  has context => (
-    is => 'rw', default => quote_sub(q{ 'list' }),
-    isa => sub {
-      my ($val) = @_;
-      die "Invalid context type $val - should be list, scalar or void"
-       unless $valid_contexts{$val};
-    },
-  );
-}
-
-has in_package => (
-  is => 'rw', default => quote_sub q{ 'Eval::WithLexicals::Scratchpad' }
-);
-
-sub eval {
-  my ($self, $to_eval) = @_;
-  local *Eval::WithLexicals::Cage::current_line;
-  local *Eval::WithLexicals::Cage::pad_capture;
-  local *Eval::WithLexicals::Cage::grab_captures;
-  my $setup = Sub::Quote::capture_unroll('$_[2]', $self->lexicals, 2);
-  my $package = $self->in_package;
-  local our $current_code = qq!use strictures 1;
-${setup}
-sub Eval::WithLexicals::Cage::current_line {
-package ${package};
-${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);
-  my @ret;
-  my $ctx = $self->context;
-  if ($ctx eq 'list') {
-    @ret = Eval::WithLexicals::Cage::current_line();
-  } elsif ($ctx eq 'scalar') {
-    $ret[0] = Eval::WithLexicals::Cage::current_line();
-  } else {
-    Eval::WithLexicals::Cage::current_line();
+=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;
   }
-  $self->lexicals({
-    %{$self->lexicals},
-    %{Eval::WithLexicals::Cage::grab_captures()}
-  });
-  @ret;
-}
-
-sub _eval_do {
-  my ($self, $text_ref) = @_;
-  local @INC = (sub {
-    if ($_[1] eq '/eval_do') {
-      open my $fh, '<', $text_ref;
-      $fh;
-    } else {
-      ();
-    }
-  }, @INC);
-  do '/eval_do' or die "Error: $@\nCompiling: $$text_ref";
-}
-
-{
-  package Eval::WithLexicals::Util;
-
-  use B qw(svref_2object);
-
-  sub capture_list {
-    my $pad_capture = \&Eval::WithLexicals::Cage::pad_capture;
-    my @names = map $_->PV, grep $_->isa('B::PV'),
-      svref_2object($pad_capture)->OUTSIDE->PADLIST->ARRAYelt(0)->ARRAY;
-    $Eval::WithLexicals::current_code .=
-      '+{ '.join(', ', map "'$_' => \\$_", @names).' };'
-      ."\n}\n}\n1;\n";
+
+  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'
+    prelude => 'use warnings',       # default 'use strictures 1'
+  );
+
+=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'
+
+=head2 prelude
+
+Code to run before evaling code. Loads L<strictures> by default.
+
+  my $current_prelude = $eval->prelude;
+
+  $eval->prelude(q{use warnings}); # only warnings, not strict.
+
+=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;