App-REPL-0.012004075500017500001750000000000001060655567200123235ustar00julianjulianApp-REPL-0.012/t004075500017500001750000000000001060655567100125655ustar00julianjulianApp-REPL-0.012/t/01-kwalitee.t010064400017500001750000000002161057643250400150430ustar00julianjulianuse Test::More; eval { require Test::Kwalitee; Test::Kwalitee->import() }; plan skip_all => 'Test::Kwalitee not installed; skipping' if $@; App-REPL-0.012/t/pod-coverage.t010064400017500001750000000002561057643665700154140ustar00julianjulian#!perl -T use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; all_pod_coverage_ok(); App-REPL-0.012/t/00-load.t010064400017500001750000000002111057606551000141460ustar00julianjulian#!perl -T use Test::More tests => 1; BEGIN { use_ok( 'App::REPL' ); } diag( "Testing App::REPL $App::REPL::VERSION, Perl $], $^X" ); App-REPL-0.012/t/pod.t010064400017500001750000000002141057606551000135770ustar00julianjulian#!perl -T use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); App-REPL-0.012/META.yml010064400017500001750000000007071060655567200136540ustar00julianjulian# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: App-REPL version: 0.012 version_from: lib/App/REPL.pm installdirs: site requires: PadWalker: 1.5 PPI: 1.118 Term::ANSIColor: 0 Test::More: 0 distribution_type: module generated_by: ExtUtils::MakeMaker version 6.30 App-REPL-0.012/iperl010075500017500001750000000137241060655406100134360ustar00julianjulian#! /usr/bin/env perl use strict; use warnings; use Term::ANSIColor ':constants'; $Term::ANSIColor::AUTORESET = 1; # ---------------------------------------------------------------------- # This first, to keep PadWalker away from lexical variables below. sub scoped_eval { print MAGENTA @_ if $App::REPL::DEBUG; eval shift; print BOLD YELLOW $@ if $@; } use PadWalker 'peek_my'; use PPI; use PPI::Find; use Data::Dumper; use Symbol; use Term::ReadLine; $App::REPL::DEBUG = 0; { my $in_package = 'App::REPL'; sub in_package { @_ ? $in_package = shift : $in_package } } # ---------------------------------------------------------------------- # Added RESET as the color somehow bleeds into the prompt # -- when we use Term::ReadLine { my $prompt; my $term = Term::ReadLine->new('iperl'); sub pnew { $prompt = RESET . in_package . ' _ ' } sub pcont { $prompt = RESET . in_package . '. ' } sub prompt { my $s = $term->readline($prompt); $term->addhistory($s) if defined($s) and $s =~ /\S/; $s } pnew; $term->ornaments(0) } sub eek { print STDERR BOLD RED @_, "\n"; goto REPL } # ---------------------------------------------------------------------- # Magic. This allows 'my' variables assigned within the eval to carry # through subsequent evals -- unless the eval'd returns from the eval, # in which case the next eval will get the same variables. #-- use constant PRO_IN => <<'EOP'; use App::REPL; use strict; no warnings 'void'; EOP sub PRO { my $r = "no strict 'refs';\n" . "package @{[in_package]};\n"; my $h = do { no strict 'refs'; ${in_package . '::REPL::env'} || {}}; for (keys %$h) { /^(.)/; $r .= "my $_ = $1" . q,{${", . in_package . q,::REPL::env"}->, . "{'$_'}};\n" } $r . PRO_IN } use constant EPI => <<'EOE'; ; no strict 'refs'; for (Symbol::qualify('')) { s/::$//; main::in_package($_) } ${main::in_package . '::REPL::env'} = PadWalker::peek_my(0) EOE # ---------------------------------------------------------------------- # More magic. This finds the final statement of some Perl, wherever # that statement may be (even if its result cannot escape the overall # evaluation), and saves its value in $App::REPL::ret #-- $App::REPL::ret = ''; { my $f = PPI::Find->new(sub { shift->isa('PPI::Statement') }); sub save_ret { my $d = shift; # don't even try if it contains something troublesome. return $d->serialize if has_troublesome($d); my @s = $f->in($d); for (reverse @s) { next if within_constructor($_, $d); print Dumper $d if $App::REPL::DEBUG > 1; unshift @{$_->{children}}, bless({content => '$App::REPL::ret'}, 'PPI::Token::Symbol'), bless({content => '='}, 'PPI::Token::Operator'); return $d->serialize } # try and save the whole thing return '$App::REPL::ret = ' . $d->serialize if @s; # give up $d->serialize } } { my %troublesome = map { $_, 1 } qw(sub package use require my our local); my $f = PPI::Find->new(sub { return 0 unless (my $e = shift)->isa('PPI::Token::Word'); return 1 if exists $troublesome{$e->{content}}; 0 }); sub has_troublesome { $f->in(shift) } } sub dump_ret { return if ref $_[0] eq 'CODE'; print BOLD CYAN Dumper $App::REPL::ret if $App::REPL::ret; } { my $fc = PPI::Find->new(sub { $_[0]->isa('PPI::Structure::Constructor') or $_[0]->isa('PPI::Structure::Block') }); sub within_constructor { my ($s, $d) = @_; my $fs = PPI::Find->new(sub { shift eq $s }); for ($fc->in($d)) { return 1 for $fs->in($_); } 0 } } # ---------------------------------------------------------------------- # The PPI here handles the rest of the magic: it detects unfinished # blocks and such so that the repl can request more lines until they # complete. Note that this does -not- handle e.g. qw( #-- { my $f = PPI::Find->new(sub { my %h = %{+shift}; (exists $h{start} and !exists $h{finish}) ? 1 : 0 }); sub repl { my $s = ''; REPL: while (defined($_ = prompt)) { $s .= "\n" . $_; my $d = PPI::Document->new(\$s); if ($f->in($d)) { pcont } else { scoped_eval PRO . save_ret($d) . EPI; dump_ret; $App::REPL::ret = ''; $s = ''; pnew } } } } # ---------------------------------------------------------------------- package App::REPL; main::repl(); # ---------------------------------------------------------------------- BEGIN { # Patch PPI 1.118 into suitability; subsequent versions should work fine. # Yes, this is somewhat wrong, and will go away as soon as PPI >1.118 # comes out -- but in these early versions of App::REPL , it should be # OK. return unless $PPI::VERSION eq 1.118; print "#-- Oh, you have PPI 1.118 -- we need to patch it up a bit.\n"; no warnings 'redefine'; package PPI::Find; sub _execute { my $self = shift; my $wanted = $self->{wanted}; my @queue = ( $self->{in} ); # Pull entries off the queue and hand them off to the wanted function while ( my $Element = shift @queue ) { my $rv = &$wanted( $Element, $self->{in} ); # Add to the matches if returns true push @{$self->{matches}}, $Element if $rv; # Continue and don't descend if it returned undef # or if it doesn't have children next unless defined $rv; next unless $Element->isa('PPI::Node'); # Add the children to the head of the queue if ( $Element->isa('PPI::Structure') ) { unshift @queue, $Element->finish if $Element->finish; unshift @queue, $Element->children; unshift @queue, $Element->start if $Element->start; } else { unshift @queue, $Element->children; } } 1; } } App-REPL-0.012/Changes010064400017500001750000000005551060655512500136700ustar00julianjulianRevision history for App-REPL 0.01 16 Mar 2007 Initial release. 0.011 17 Mar 2007 Move scoped_eval about to get slightly saner treatment of variables in iperl. Makefile.PL now has an ABSTRACT App::REPL points towards the README for real documentation. 0.012 11 Apr 2007 Very small changes to support colorless operation. App-REPL-0.012/lib004075500017500001750000000000001060655567100130705ustar00julianjulianApp-REPL-0.012/lib/App004075500017500001750000000000001060655567100136105ustar00julianjulianApp-REPL-0.012/lib/App/REPL.pm010064400017500001750000000057641060655453600147770ustar00julianjulianpackage App::REPL; use warnings; use strict; use Data::Dumper; use PadWalker 'peek_my'; use Term::ANSIColor ':constants'; $Term::ANSIColor::AUTORESET = 1; require Exporter; use vars qw(@ISA @EXPORT $VERSION); $VERSION = '0.012'; @ISA = qw(Exporter); @EXPORT = qw(x p env ret rdebug help); # ---------------------------------------------------------------------- sub x { print Dumper @_ } sub p { print @_, "\n" } sub env { peek_my(1) } sub ret { $App::REPL::ret } sub rdebug { if (@_) { $App::REPL::DEBUG = shift } else { $App::REPL::DEBUG++ } print YELLOW "Debug level set to $App::REPL::DEBUG\n" } sub help { if (@_ and shift eq 'commands') { system perldoc => 'App::REPL' } else { print YELLOW < EOH } } 1; __END__ # ---------------------------------------------------------------------- =head1 NAME App::REPL - A container for functions for the iperl program =head1 VERSION Version 0.01 =head1 SYNOPSIS This module contains functions that the iperl program automatically imports into any package it enters, for interactive convenience. Please see the README for general information. =head1 EXPORT =head1 FUNCTIONS =head2 x Print arguments with C =head2 p Print arguments with C =head2 env Return a hashref containing the (stored -- not current) lexical environment. =head2 ret Return a reference to the value of the previous evaluation -- that is, a reference to whatever irepl printed after the last Perl you evaluated. This function will probably evolve to take an argument C<$n>, to return the C<$n>'th previous result. =head2 rdebug (C<$value>) With no arguments, bump C<$REPL::DEBUG>. With an argument, set C<$REPL::DEBUG> to that. This is for debugging iperl itself; currently at 1 it shows eval'd code, and at 2 it dumps the PPI document corresponding to entered code. =head2 help (commands) With no arguments, print a brief message. With an argument, either print corresponding help or -- in the case of C<'commands'>, currently the only optional argument -- call perldoc appropriately. =head1 AUTHOR Julian Fondren, C<< >> =head1 BUGS Does not reliably report errors in eval'd code. Does not try hard enough to collect a return value from eval'd code. Makes probably dangerous use of PPI. Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 VERSION CONTROL A subversion repository with anonymous checkout exists at http://OpenSVN.csie.org/app_repl , and you can also browse the repository from that URL with a web browser. =head1 COPYRIGHT & LICENSE Copyright 2007 Julian Fondren, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. App-REPL-0.012/MANIFEST010064400017500001750000000001701057643733200135240ustar00julianjulianChanges MANIFEST Makefile.PL README META.yml lib/App/REPL.pm iperl t/00-load.t t/01-kwalitee.t t/pod-coverage.t t/pod.t App-REPL-0.012/Makefile.PL010064400017500001750000000012761057660414600143540ustar00julianjulianuse strict; use warnings; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'App::REPL', AUTHOR => 'Julian Fondren ', ABSTRACT => 'A read-eval-print-loop for Perl', VERSION_FROM => 'lib/App/REPL.pm', ABSTRACT => 'A read-eval-print-loop for Perl', EXE_FILES => [ 'iperl' ], PREREQ_PM => { 'Test::More' => 0, 'Term::ANSIColor' => 0, 'PadWalker' => 1.5, 'PPI' => 1.118, # buggy, but we monkeypatch it }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => 'App-REPL-*' }, ); App-REPL-0.012/README010064400017500001750000000056571060655471600132720ustar00julianjulianApp-REPL v0.012 0. The install worked, but 'iperl' is complaining that it can't find App::REPL ! Why do you hate me? Try again -- it'll work this time. Sorry, I've no idea why this happens. I can't reproduce it after it happens the once. 1. What does the name mean? Read-Eval-Print-Loop. App-REPL offers an application that reads Perl, evaluates it, and tries to print a result. 2. Tries to? It doesn't bother with 'CODE' refs, for instance. 3. How does it do that at all? Can it only handle simple expressions? It uses PPI to find a good place within the given Perl to insert an assignment. It isn't perfect, and probably you can find places where it doesn't print anything useful. You can type arbitrarily complex Perl, though -- with 'package' statements, subroutine definitions, &c. 4. ... all on a single line? :-/ No. Again, it uses PPI to try and determine if your Perl is complete, and if not it will continue to prompt you for the rest. 5. Tries to? Well, Perl isn't Python :-) Without imposing annoying restrictions, App-REPL can't know that you meant for the second line to continue on from the first, in: print $a, $b, $c, $d; One error in the current version is that App-Perl doesn't notice that this is incomplete: print qw(a b It -can- notice, but PPI currently handles quote-like operators strangely, treating qw## and qw() in oddly different ways, and I want to either resolve that or resolve my thinking about that before I add further magic to App-REPL 6. You say 'magic', but I hear 'fragile evil'. What does App-REPL do that's fragile and evil? Well, several things. 1. The current version monkey-patches a bug out of PPI, so that I don't have to wait for the next PPI to be released before I can get comments on this (alpha, anyway) version of App-REPL. 2. It relies on the way PPI structures things, and -modifies- that structure without PPI's knowledge, in ways that I figured out through trial and Data::Dumper 3. It uses PadWalker 7. Yow. Anything else I should worry about? Everything you enter will be eval'd with lengthy prologue and epilogue code, first to set up the environment and finally to remember what you did to the environment. Although this system -does- try to handle packages well, it currently only recognizes the final package of your eval. So, if you do: package A; my $b = 1; package B; '$b' will not be available for your next eval. 8. You know, your colors are very annoying. Can I turn them off? Sure, just set the ANSI_COLORS_DISABLED environment variable that Term::ANSIColor checks. INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install COPYRIGHT AND LICENCE Copyright (C) 2007 Julian Fondren This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.