Telephone +44(0)1524 64544
Email: info@shadowcat.co.uk

lpw-2010 - the-devils-repl

Sat Dec 22 00:30:00 2012

Slides for the talk the-devils-repl at lpw-2010

The
Devil's
REPL

-

5 minutes
65 slides
1 crack pipe

-

Devel::REPL

-

written for
a series
of blog posts

-

Moose
MooseX::Object::Pluggable

-

Works
great

-

FATTER
THAN
CARTMAN

-

Shitloads
of XS

-

FAIL

-

Minimalist
repl?

-

Pure perl
repl!

-

But I need
lexical
persistence

-

Lexical::Persistence

-

PadWalker
Devel::LexAlias

-

Hmm ...

-

 eval q{sub {
   my $x; $x++; $x
 }}

-

 use B;
 my $obj = svref_2object($sub);

-

 $obj->PADLIST

-

 $obj->PADLIST->ARRAY

-

  [
    'B::SPECIAL=SCALAR(0x1a47510)',
    'B::PVNV=SCALAR(0x1a47570)',
    'B::SPECIAL=SCALAR(0x1a475d0)'
  ],
  [
    'B::AV=SCALAR(0x1a47678)',
    'B::IV=SCALAR(0x1a476a8)',
    'B::NULL=SCALAR(0x1a47708)'
  ]

-

 [ \@names,
   \@values ]

-

  map $_->PV, grep $_->isa('B::PV'),
    $obj->PADLIST->ARRAYelt(0)->ARRAY;

-

Values are
useless

-

Values are
already
DESTROYed

-

Hmm ...

-

 BEGIN { our $y = \$x; }

-

Works at
toplevel

-

 sub {
   ...
   BEGIN { our $y = \$x; }
 }

-

Variable "$x"
is not
available

-

:(

-

  sub {
    ...
    capture_pads()
  }

-

Breaks
return
values

-

Hmm ...

-

  sub {
    my $guard = bless({}, 'HasADESTROY');
    ...
  }

-

Lexicals
are freed
in reverse
order of
declaration

-

So $guard
is DESTROYed
too late

-

:(

-

Hmm ...

-

  sub {
    ...
    sub aha { our $y = \$x }
  }

-

Variable $x
will not
stay shared

-

... *stay* ...

-

I only need
to share it
the once!

-

Now how do I
get the list?

-

  sub {
    ...
    sub inside { }
    BEGIN { inspect_inside() }
    ...

-

  my $obj = svref_2object(
    \&inside
  );

-

  map $_->PV, grep $_->isa('B::PV'),
    $obj->PADLIST->ARRAYelt(0)->ARRAY;

-

But how do
I inject the
capture code?

-

  sub {
    ...
    sub inside { }
    BEGIN { inspect_inside() }
    __NOW_CAPTURIZE__

-

  s{__NOW_CAPTURIZE__}
   {$capture_code}

-

Source
filter?

-

Source filters
don't work
in string eval

-

:(

-

But ...

-

coderef
in @INC

-

... opening
a string
filehandle

-

  local @INC = (sub {
    if ($_[1] eq '/eval_do') {
      open my $fh, '<', $text_ref;
      return $fh;
    }
  }, @INC);

-

The BEGIN
happens
before that
line's read

-

And my s{}{}
will work!

-

Or even an
append

-

  $code .= '+{ '
    .join(', ',
      map "'$_' => \\$_", @names
    )
  .' };'

-

  my $x = ${$_[2]->{"\$x"}};
  sub Eval::WithLexicals::Cage::current_line {
    package Eval::WithLexicals::Scratchpad;
    ++$x
    ;sub Eval::WithLexicals::Cage::pad_capture { }
    BEGIN { Eval::WithLexicals::Util::capture_list() }
    sub Eval::WithLexicals::Cage::grab_captures {
      no warnings 'closure';
      package Eval::WithLexicals::Cage;+{ '$x' => \$x };
    }
  }

-

  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; try {
      local $SIG{INT} = sub { die "Caught SIGINT" };
      @ret = $eval->eval($line);
    } catch {
      @ret = ("Error!", $_);
    };
    print Dumper @ret;
  }

-

  re.pl$ my $x = 1
  1
  re.pl$ ++$x
  2
  re.pl$ $x
  2

-

Eval::WithLexicals
went to CPAN during
the LPW hackathon

-

  $ cpanm Eval::WithLexicals
  $ tinyrepl
  re.pl$

-

Thank you.