Shell-Perl-0.0026/000755 000765 000024 00000000000 13034520602 014505 5ustar00ferreirastaff000000 000000 Shell-Perl-0.0026/bin/000755 000765 000024 00000000000 13034520601 015254 5ustar00ferreirastaff000000 000000 Shell-Perl-0.0026/Changes000644 000765 000024 00000011145 13034520365 016010 0ustar00ferreirastaff000000 000000 Revision history for Perl extension Shell::Perl. 0.0026 Sun Jan 08 12:40 PT 2017 - fix t/20expect_quit.t when TERM is not set 0.0025 Sat Jan 07 11:59 PT 2017 - requires version 0.77 - requires Getopt::Long 2.43 – bundling_values 0.0024 Fri Jan 06 2017 - every statement is now evalued by default with the current perl version running the shell - new shell command :set perl_version - change in CLI options: case-sensitivity & no auto abbreviations - pirl -I dir1 -Idir2 - drop File::Slurp as a prereq 0.0023 Fri Jan 14:05:00 2014 - Update Makefile.PL from 'use 5.006' to 'use 5.008' as per RT#43117. Thanx ANDK. - Fix dates in this file. 0.0022 Sat Mar 12 12:00:00 2011 - no real change - Fix test again for 5.6 sake - Improved Makefile.PL / META.yml 0.0021 Sat Mar 12 12:00:00 2011 - no real change - New test fix for happy 5.6 testing 0.0020 Thu Mar 10 12:00:00 2011 - no real change - Test fixes for keeping 5.6 happy 0.0019 Wed Mar 09 12:00:00 2011 - Dumper updated to work with Data::Dump 1.16+ 0.0018 Sat Oct 25 12:00:00 2008 - fix t/11version.t to deal with IPC::Cmd $err as an error message (undef on success) rather than an error code (0 on success) [Thanks, Andreas, for spotting that] http://rt.cpan.org/Ticket/Display.html?id=40157 0.0017 Sun Mar 09 12:00:00 2008 - assume this is not supposed to work for Perls under 5.6.0 - cope with noisy 5.6 blib at t/20expect_quit.t (illustrated by test report http://www.nntp.perl.org/group/perl.cpan.testers/2008/03/msg1099937.html) - pirl now accepts switches --version and -v - added IPC::Cmd and Test::Deep as prerequisites for testing 0.0016 Mon Mar 03 12:00:00 2008 - quit is now a method - history is now persistent across sections (should work with T::RL::Gnu and T::RL::Perl) - new dependencies: File::HomeDir, Path::Class, and File::Slurp 0.0015 Fri Jul 27 12:00:00 2007 - fixed 'quit' - the first of the Expect tests: t/20expect_quit.t - pirl now accepts switches --ornaments and --noornaments 0.0014 Thu Jul 26 12:00:00 2007 - Shell::Perl now prints to the output stream of the associated term - new test t/10compile.t - :x is the same as :exit, :quit, :q - quit is an alias to "sub { exit }" in the sandbox package 0.0013 Thu Jun 21 12:00:00 2007 - the dumpers now deparse Perl code - added new dumper based on Data::Dump::Streamer, no docs by now (try it with ":set out DDS") 0.0012 Thu Jun 21 12:00:00 2007 - added an experimental "dump history" command (RT #26973, by mgrimes) - hopefully get rid of the double newline in eval output 0.0011 Thu Jun 21 12:00:00 2007 - some minor improvements to Makefile.PL - conformance to META.yml specification - when dumping YAML, prefer YAML::Syck to YAML if available - we have a bug to fix: the REPL and the running interpreter share global state (like $_) and so relying on these between lines is not nice (to say the least) 0.0010 Wed Mar 14 12:00:00 2007 - this is a read-eval-print loop and not a read-eval-loop, doh - total rewrite for the implementation of dumpers - new output style with the plain dumper (idea borrowed from Sepia by Sean O'Rourke) - Data::Dump, Data::Dumper, YAML are only required at runtime and fail gracefully if not there - the preferred dumper is via Data::Dump, but it falls back to Data::Dumper, YAML and the plain dumper according to availability - fix bug: inline contexts (like #scalar) were not being respected for printing - Makefile.PL required ExtUtils::MakeMaker 6.31 and we are more tolerant now (thanks, Lorn) - now the right line number is used at warnings and errors (Lorn again) 0.0009 Tue Mar 13 12:00:00 2007 - forgot README in MANIFEST - the default evaluation package is now "Shell::Perl::sandbox" 0.0008 Tue Mar 13 12:00:00 2007 - cmarcelo: some doc typos fixed - a SEE ALSO section - first CPAN release 0.0007 Wed Mar 07 12:00:00 2007 - added some POD to Shell.pm and pirl source files 0.0006 Fri Feb 23 12:00:00 2007 - sources imported to Google code (no history :(, yet) - patches by cmarcelo: * implement list and void contexts for evaluation * implement context override in a per-input basis - we have tests now, even though t/98pod-coverage.t does not pass yet 0.0005 Sat Jan 27 12:00:00 2007 - relax the restrictions during eval - no strict qw(vars subs) 0.0004 Thu Jan 25 12:00:00 2007 - the same changes as before, now working 0.0003 Thu Jan 25 12:00:00 2007 - prompt changes with the script name - psh renamed to pirl - borked 0.0002 Wed Jan 24 12:00:00 2007 - Date fabricated. 0.0001 Tue Jan 23 12:00:00 2007 - Date fabricated. Shell-Perl-0.0026/lib/000755 000765 000024 00000000000 13034520601 015252 5ustar00ferreirastaff000000 000000 Shell-Perl-0.0026/Makefile.PL000644 000765 000024 00000002231 13034233514 016460 0ustar00ferreirastaff000000 000000 use strict; use warnings; use 5.008; use ExtUtils::MakeMaker; my $EUMM_VERSION = eval $ExtUtils::MakeMaker::VERSION; WriteMakefile ( NAME => 'Shell::Perl', VERSION_FROM => 'lib/Shell/Perl.pm', EXE_FILES => [ 'bin/pirl' ], PREREQ_PM => { 'Class::Accessor' => 0, 'File::Basename' => 0, 'File::HomeDir' => 0, 'Getopt::Long' => 2.43, # bundling_values 'IPC::Cmd' => 0, 'Path::Class' => 0.34, # spew_lines() 'Test::Deep' => 0, 'Test::More' => 0, 'Term::ReadLine' => 0, 'version' => 0.77, # parse() }, ($] >= 5.005 ? ( ABSTRACT_FROM => 'lib/Shell/Perl.pm', AUTHOR => 'A. R. Ferreira ', ) : ()), ($EUMM_VERSION >= 6.31 ? ( LICENSE => 'perl', ) : ()), ($EUMM_VERSION > 6.4501 ? ( META_MERGE => { recommends => { # optional tests 'Test::Pod' => 0, 'Test::Pod::Coverage' => 0, 'Test::Script' => 0, 'Test::Expect' => 0, }, resources => { repository => 'http://github.com/aferreira/pirl', }, }, ) : ()), ); # recommended: # # - -one_of: [ YAML::Syck, YAML ] # - Data::Dump # - Data::Dumper # - Data::Dump::Streamer # Shell-Perl-0.0026/MANIFEST000644 000765 000024 00000000607 13034520602 015641 0ustar00ferreirastaff000000 000000 bin/pirl lib/Shell/Perl.pm lib/Shell/Perl/Dumper.pm Makefile.PL MANIFEST This list of files Changes README t/01use.t t/02basic.t t/10compile.t t/11version.t t/20expect_quit.t t/50isolated.t t/90pod.t t/98pod-coverage.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Shell-Perl-0.0026/META.json000644 000765 000024 00000002706 13034520602 016133 0ustar00ferreirastaff000000 000000 { "abstract" : "A read-eval-print loop in Perl", "author" : [ "A. R. Ferreira " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150001", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Shell-Perl", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "recommends" : { "Test::Expect" : "0", "Test::Pod" : "0", "Test::Pod::Coverage" : "0", "Test::Script" : "0" }, "requires" : { "Class::Accessor" : "0", "File::Basename" : "0", "File::HomeDir" : "0", "Getopt::Long" : "2.43", "IPC::Cmd" : "0", "Path::Class" : "0.34", "Term::ReadLine" : "0", "Test::Deep" : "0", "Test::More" : "0", "version" : "0.77" } } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "http://github.com/aferreira/pirl" } }, "version" : "0.0026" } Shell-Perl-0.0026/META.yml000644 000765 000024 00000001513 13034520602 015756 0ustar00ferreirastaff000000 000000 --- abstract: 'A read-eval-print loop in Perl' author: - 'A. R. Ferreira ' build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150001' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Shell-Perl no_index: directory: - t - inc recommends: Test::Expect: '0' Test::Pod: '0' Test::Pod::Coverage: '0' Test::Script: '0' requires: Class::Accessor: '0' File::Basename: '0' File::HomeDir: '0' Getopt::Long: '2.43' IPC::Cmd: '0' Path::Class: '0.34' Term::ReadLine: '0' Test::Deep: '0' Test::More: '0' version: '0.77' resources: repository: http://github.com/aferreira/pirl version: '0.0026' Shell-Perl-0.0026/README000644 000765 000024 00000001522 13034520444 015371 0ustar00ferreirastaff000000 000000 Shell-Perl version 0.0026 =========================== This is the implementation of a simple command-line interpreter for Perl. After installing the module, you invoke it like this $ pirl Welcome to the Perl shell. Type ':help' for more information pirl @> or in Windows > pirl INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: Term:ReadLine Data::Dumper YAML or YAML::Syck Data::Dump Class::Accessor and recommends Test::Pod 1.18 Test::Pod::Coverage 1.04 Test::Script COPYRIGHT AND LICENCE Copyright (C) 2007-2017 by Adriano R. Ferreira This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Shell-Perl-0.0026/t/000755 000765 000024 00000000000 13034520601 014747 5ustar00ferreirastaff000000 000000 Shell-Perl-0.0026/t/01use.t000755 000765 000024 00000000202 13033757015 016100 0ustar00ferreirastaff000000 000000 #!perl -T use Test::More tests => 1; use_ok('Shell::Perl'); diag( "Testing Shell::Perl $Shell::Perl::VERSION, Perl $], $^X" ); Shell-Perl-0.0026/t/02basic.t000755 000765 000024 00000000250 13033757015 016371 0ustar00ferreirastaff000000 000000 #!perl -T use Test::More tests => 3; BEGIN { use_ok('Shell::Perl'); } my $sh = Shell::Perl->new(); ok($sh, 'defined return of new()'); isa_ok($sh, 'Shell::Perl'); Shell-Perl-0.0026/t/10compile.t000644 000765 000024 00000000253 13033757015 016737 0ustar00ferreirastaff000000 000000 use Test::More; eval "use Test::Script"; plan skip_all => "Test::Script required for testing" if $@; plan( tests => 1 ); script_compiles( 'bin/pirl', 'pirl compiles'); Shell-Perl-0.0026/t/11version.t000644 000765 000024 00000001165 13033757015 017000 0ustar00ferreirastaff000000 000000 use Test::More tests => 8; my @pirl = ( $^X, '-Mblib', 'blib/script/pirl' ); use IPC::Cmd qw( run ); use Test::Deep; for my $switch ( '-v', '--version' ) { my ( $ok, $err, $full_buf, $out_buf, $err_buf ) = run( command => [ @pirl, $switch ] ); ok( $ok, "'pirl $switch' run ok" ); ok( !$err, 'no error'); cmp_deeply( $out_buf, [ re(qr/\AThis is pirl/) ], 'printed version info' ); my $NO_STDERR_OUTPUT = ($] < 5.008) ? [re(qr/Using .* lib \n/msx)] # cope with noisy 5.6 blib : []; cmp_deeply( $err_buf, $NO_STDERR_OUTPUT, 'no output to STDERR' ) or diag("err_buf= (@$err_buf)"); } Shell-Perl-0.0026/t/20expect_quit.t000644 000765 000024 00000001253 13034517473 017646 0ustar00ferreirastaff000000 000000 use Test::More; eval "use Test::Expect"; plan skip_all => "Test::Expect required for testing" if $@; # test pirl and its many quit commands plan( tests => 2*6 ); unless ($ENV{TERM}) { # help when TERM is not setup diag qq{TERM not set, using "dumb"}; $ENV{TERM} = 'dumb'; } for my $quit_command ( ':quit', ':q', ':exit', ':x', 'exit', 'quit' ) { expect_run( command => "$^X -Mblib blib/script/pirl --noornaments", prompt => 'pirl @> ', quit => $quit_command, ); expect_like( qr/\A (?: Using .*? blib \n )? # cope with noisy 5.6 blib Welcome /msx, "welcome message" ); } Shell-Perl-0.0026/t/50isolated.t000644 000765 000024 00000001063 13033757015 017117 0ustar00ferreirastaff000000 000000 #perl -T use Test::More 'no_plan'; BEGIN { use_ok('Shell::Perl'); } my $sh = Shell::Perl->new; $_ = ' $_ = 1000 '; my $val = $sh->eval($_); is($val, 1000); $_ = ' $_ '; my $val2 = $sh->eval($_); TODO: { local $TODO = 'needs separating the REPL and the interpreter states'; is($val2, 1000); } # this test script touches at a very sensitive # issue in the implementation of a REPL - # the state of the loop must be kept separate # from the state of the running interpreter. # By state, we mean those global variables like # $_ and everything else. Shell-Perl-0.0026/t/90pod.t000755 000765 000024 00000000253 13033757015 016104 0ustar00ferreirastaff000000 000000 #!perl -T use strict; use Test::More; eval "use Test::Pod 1.18"; plan skip_all => "Test::Pod 1.18 required for testing POD" if $@; all_pod_files_ok(all_pod_files(".")); Shell-Perl-0.0026/t/98pod-coverage.t000644 000765 000024 00000000255 13033757015 017704 0ustar00ferreirastaff000000 000000 #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(); Shell-Perl-0.0026/lib/Shell/000755 000765 000024 00000000000 13034520601 016321 5ustar00ferreirastaff000000 000000 Shell-Perl-0.0026/lib/Shell/Perl/000755 000765 000024 00000000000 13034520601 017223 5ustar00ferreirastaff000000 000000 Shell-Perl-0.0026/lib/Shell/Perl.pm000644 000765 000024 00000050465 13034520453 017600 0ustar00ferreirastaff000000 000000 package Shell::Perl; use strict; use warnings; our $VERSION = '0.0026'; use base qw(Class::Accessor); # soon use base qw(Shell::Base); Shell::Perl->mk_accessors(qw( out_type dumper context package perl_version term ornaments library )); # XXX use_strict use lib (); use Getopt::Long 2.43 qw(:config no_auto_abbrev no_ignore_case bundling_values); use version 0.77; use Term::ReadLine; use Shell::Perl::Dumper; # out_type defaults to one of 'D', 'DD', 'Y', 'P'; # dumper XXX # context defaults to 'list' # package defaults to __PACKAGE__ . '::sandbox' # XXX use_strict defaults to 0 sub new { my $self = shift; my $sh = $self->SUPER::new({ context => 'list', # print context perl_version => $], @_ }); $sh->_init; return $sh; } my %dumper_for = ( 'D' => 'Shell::Perl::Data::Dump', 'DD' => 'Shell::Perl::Data::Dumper', 'Y' => 'Shell::Perl::Dumper::YAML', 'Data::Dump' => 'Shell::Perl::Data::Dump', 'Data::Dumper' => 'Shell::Perl::Data::Dumper', 'YAML' => 'Shell::Perl::Dumper::YAML', 'DDS' => 'Shell::Perl::Data::Dump::Streamer', 'P' => 'Shell::Perl::Dumper::Plain', 'plain' => 'Shell::Perl::Dumper::Plain', ); sub _init { my $self = shift; # loop until you find one available alternative for dump format my $dumper_class; for my $format ( qw(D DD DDS Y P) ) { if ($dumper_for{$format}->is_available) { #$self->print("format: $format\n"); $self->set_out($format); last } # XXX this is not working 100% - and I have no clue about it } # Set library paths if ($self->library) { warn "Setting library paths (@{$self->library})\n"; lib->import(@{ $self->library }); } $self->set_package( __PACKAGE__ . '::sandbox' ); } sub _shell_name { require File::Basename; return File::Basename::basename($0); } sub print { my $self = shift; print {$self->term->OUT} @_; } ## # XXX remove: code and docs ## sub out { ## my $self = shift; ## ## # XXX I want to improve this: preferably with an easy way to add dumpers ## if ($self->context eq 'scalar') { ## $self->print($self->dumper->dump_scalar(shift), "\n"); ## } else { # list ## $self->print($self->dumper->dump_list(@_), "\n"); ## } ## } # XXX I want to improve this: preferably with an easy way to add dumpers =begin private =item B<_print_scalar> $sh->_print_scalar($answer); That corresponds to the 'print' in the read-eval-print loop (in scalar context). It outputs the evaluation result after passing it through the current dumper. =end private =cut sub _print_scalar { # XXX make public, document my $self = shift; $self->print($self->dumper->dump_scalar(shift)); } =begin private =item B<_print_scalar> $sh->_print_list(@answers); That corresponds to the 'print' in the read-eval-print loop (in list context). It outputs the evaluation result after passing it through the current dumper. =end private =cut sub _print_list { # XXX make public, document my $self = shift; $self->print($self->dumper->dump_list(@_)); } sub _warn { shift; my $shell_name = _shell_name; warn "$shell_name: ", @_, "\n"; } sub set_out { my $self = shift; my $type = shift; my $dumper_class = $dumper_for{$type}; if (!defined $dumper_class) { $self->_warn("unknown dumper $type"); return; } if ($dumper_class->is_available) { $self->dumper($dumper_class->new); $self->out_type($type); } else { $self->_warn("can't load dumper $dumper_class"); } } sub _ctx { my $context = shift; if ($context =~ /^(s|scalar|\$)$/i) { return 'scalar'; } elsif ($context =~ /^(l|list|@)$/i) { return 'list'; } elsif ($context =~ /^(v|void|_)$/i) { return 'void'; } else { return undef; } } sub set_ctx { my $self = shift; my $context = _ctx shift; if ($context) { $self->context($context); } else { $self->_warn("unknown context $context"); } } sub set_package { my $self = shift; my $package = shift; if ($package =~ /( [a-zA-Z_] \w* :: )* [a-zA-Z_] \w* /x) { $self->package($package); no strict 'refs'; *{ "${package}::quit" } = sub { $self->quit }; } else { $self->_warn("bad package name $package"); } } # $err = _check_perl_version($version); sub _check_perl_version { my $version = shift; my $ver = eval { version->parse($version) }; if ($@) { (my $err = $@) =~ s/at \S+ line \d+.$//; return $err; } # Current perl my $v = $^V || version->parse($]); if ($ver > $v) { return "This is only $v"; } return undef; # good } sub set_perl_version { my $self = shift; my $version = shift; if (!defined $version) { $self->perl_version($]); } elsif ($version eq q{''} || $version eq q{""}) { $self->perl_version(''); } else { my $err = _check_perl_version($version); if ($err) { $self->_warn("bad perl_version ($version): $err"); } else { $self->perl_version($version); } } } use constant HELP => <<'HELP'; Shell commands: (begin with ':') :e(x)it or :q(uit) - leave the shell :set out (D|DD|DDS|Y|P) - setup the output format :set ctx (scalar|list|void|s|l|v|$|@|_) - setup the eval context :set package - set package in which shell eval statements :set perl_version - set perl version to eval statements :reset - reset the environment :dump history - (experimental) print the history to STDOUT or a file :h(elp) - get this help screen HELP sub help { print HELP; } # :reset is a nice idea - but I wanted more like CPAN reload # I retreated the current implementation of :reset # because %main:: is used as the evaluation package # and %main:: = () is too severe by now sub reset { my $self = shift; my $package = $self->package; return if $package eq 'main'; # XXX don't reset %main:: no strict 'refs'; %{"${package}::"} = (); #%main:: = (); # this segfaults at my machine } sub prompt_title { my $self = shift; my $shell_name = _shell_name; my $sigil = { scalar => '$', list => '@', void => '' }->{$self->{context}}; return "$shell_name $sigil> "; } sub _readline { my $self = shift; return $self->term->readline($self->prompt_title); } sub _history_file { # XXX require Path::Class; require File::HomeDir; return Path::Class::file( File::HomeDir->my_home, '.pirl-history-xxx' ); } sub _read_history { # XXX belongs to Shell::Perl::ReadLine my $term = shift; my $h = _history_file; #warn "read history from $h\n"; # XXX if ( $term->Features->{readHistory} ) { $term->ReadHistory( "$h" ); } elsif ( $term->Features->{setHistory} ) { if ( -e $h ) { my @h = $h->slurp( chomp => 1 ); $term->SetHistory( @h ); } } else { # warn "Your ReadLine doesn't support setHistory\n"; } } sub _write_history { # XXX belongs to Shell::Perl::ReadLine my $term = shift; my $h = _history_file; #warn "write history to $h\n"; # XXX if ( $term->Features->{writeHistory} ) { $term->WriteHistory( "$h" ); } elsif ( $term->Features->{getHistory} ) { my @h = $term->GetHistory; $h->spew_lines(\@h); } else { # warn "Your ReadLine doesn't support getHistory\n"; } } sub _new_term { my $self = shift; my $name = shift; my $term = Term::ReadLine->new( $name ); _read_history( $term ); return $term; } sub run { my $self = shift; my $shell_name = _shell_name; $self->term( my $term = $self->_new_term( $shell_name ) ); $term->ornaments($self->ornaments); # XXX my $prompt = "$shell_name > "; print "Welcome to the Perl shell. Type ':help' for more information\n\n"; REPL: while ( defined ($_ = $self->_readline) ) { # trim s/^\s+//g; s/\s+$//g; # Shell commands start with ':' followed by something else # which is not ':', so we can use things like '::my_subroutine()'. if (/^:[^:]/) { last REPL if /^:(exit|quit|q|x)/; $self->set_out($1) if /^:set out (\S+)/; $self->set_ctx($1) if /^:set ctx (\S+)/; $self->set_package($1) if /^:set package (\S+)/; $self->set_perl_version($1) if /^:set perl_version(?: (\S+))?/; $self->reset if /^:reset/; $self->help if /^:h(elp)?/; $self->dump_history($1) if /^:dump history(?:\s+(\S*))?/; # unknown shell command ?! next REPL; } my $context; $context = _ctx($1) if s/#(s|scalar|\$|l|list|\@|v|void|_)\z//; $context = $self->context unless $context; if ( $context eq 'scalar' ) { my $out = $self->eval($_); if ($@) { warn "ERROR: $@"; next } $self->_print_scalar($out); } elsif ( $context eq 'list' ) { my @out = $self->eval($_); if ($@) { warn "ERROR: $@"; next } $self->_print_list(@out); } elsif ( $context eq 'void' ) { $self->eval($_); if ($@) { warn "ERROR: $@"; next } } else { # XXX should not happen } } $self->quit; } sub _package_stmt { my $package = shift->package; ("package $package"); } sub _use_perl_stmt { my $perl_version = shift->perl_version; $perl_version ? ("use $perl_version") : (); } # $shell->eval($exp) sub eval { my $self = shift; my $exp = shift; my $preamble = join ";\n", ( $self->_package_stmt, $self->_use_perl_stmt, "no strict qw(vars subs)", "", # for the trailing ; ); # XXX gotta restore $_, etc. return eval <term ); $self->print( "Bye.\n" ); # XXX exit; } sub run_with_args { my $self = shift; # XXX do something with @ARGV (Getopt) my %options = ( ornaments => 1 ); if ( @ARGV ) { # only require Getopt::Long if there are actually command line arguments require Getopt::Long; Getopt::Long::GetOptions( \%options, 'ornaments!', 'version|v', 'library|I=s@' ); } my $shell = Shell::Perl->new(%options); if ( $options{version} ) { $shell->_show_version; } else { $shell->run; } } sub _show_version { my $self = shift; printf "This is %s, version %s (%s, using Shell::Perl %s)\n", _shell_name, $main::VERSION, $0, $Shell::Perl::VERSION; exit 0; } sub dump_history { my $self = shift; my $file = shift; if ( !$self->term->Features->{getHistory} ) { print "Your Readline doesn't support getHistory\n"; return; } if ( $file ) { open( my $fh, ">>", $file ) or do { warn "Couldn't open '$file' for history dump\n"; return; }; for ( $self->term->GetHistory ) { print $fh $_, "\n"; } close $fh; print "Dumped history to '$file'\n\n"; } else { print $_, "\n" for($self->{term}->GetHistory); print "\n"; } return 1; } 1; # OUTPUT Data::Dump, Data::Dumper, YAML, others # document: use a different package when eval'ing # reset the environment # implement shell commands (:quit, :set, :exit, etc.) # how to implement array contexts? # IDEA: command ":set ctx scalar | list | void" # terminators "#s" "#l" "#v" "#$" #@ #_ # allow multiline entries. how? ##sub set {} # sets up the instance variables of the shell ## ##sub run {} # run the read-eval-print loop ## ##sub read {} # read a chunk ## ##sub readline {} # read a line ## ##sub eval {} ## ##sub print {} ## ##sub warn {} ## ##sub help { shift->print(HELP) } ## ##sub out { ? } __END__ =pod =encoding utf-8 =head1 NAME Shell::Perl - A read-eval-print loop in Perl =head1 SYNOPSYS use Shell::Perl; Shell::Perl->run_with_args; =head1 DESCRIPTION This is the implementation of a command-line interpreter for Perl. I wrote this because I was tired of using B when needing a calculator with a real language within. Ah, that and because it was damn easy to write it. This module is the heart of the B script provided with B distribution, along with this module. =head2 EXAMPLE SESSION $ pirl Welcome to the Perl shell. Type ':help' for more information pirl @> 1+1 2 pirl @> use YAML qw(Load Dump); () pirl @> $data = Load("--- { a: 1, b: [ 1, 2, 3] }\n"); { a => 1, b => [1, 2, 3] } pirl @> $var = 'a 1 2 3'; $var =~ /(\w+) (\d+) (\d+)/ ("a", 1, 2) pirl @> :q =head2 COMMANDS Most of the time, the shell reads Perl statements, evaluates them and outputs the result. There are a few commands (started by ':') that are handled by the shell itself. =over 4 =item :h(elp) Handy for remembering what the shell commands are. =item :q(uit) Leave the shell. The Perl statement C will work too. SYNONYMS: :exit, :x =item :set out (D|DD|DDS|Y|P) Changes the dumper for the expression results used before output. The current supported are: =over 4 =item D C =item DD C, the good and old core module =item DDS C =item Y C =item P a plain dumper ("$ans" or "@ans") =back When creating the shell, the dump format is searched among the available ones in the order "D", "DD", "DDS", "Y" and "P". That means L is preferred and will be used if available/installed. Otherwise, L is tried, and so on. Read more about dumpers at L. =item :set ctx (scalar|list|void|s|l|v|$|@|_) Changes the default context used to evaluate the entered expression. The default is C<'list'>. Intuitively, 'scalar', 's' and '$' are synonyms, just like 'list', 'l', and '@' or 'void', 'v', '_'. There is a nice way to override the default context in a given expression. Just a '#' followed by one of 'scalar|list|void|s|l|v|$|@|_' at the end of the expression. pirl @> $var = 'a 1 2 3'; $var =~ /(\w+) (\d+) (\d+)/ ("a", 1, 2) pirl @> $var = 'a 1 2 3'; $var =~ /(\w+) (\d+) (\d+)/ #scalar 1 =item :set perl_version Changes the perl version (and current feature bundle) used to evaluate each statement. Usage examples are: :set perl_version 5.008 :set perl_version v5.10 :set perl_version # current perl version, $] Default is to use the current perl version, which works like C. Set to an empty string, as in :set perl_version '' for the behavior of pirl 0.0023 or earlier. =item :reset Resets the environment, erasing the symbols created at the current evaluation package. See the section L<"ABOUT EVALUATION">. =back =head2 METHODS Remember this is an alpha version, so the API may change and that includes the methods documented here. So consider this section as implementation notes for a while. In later versions, some of these information may be promoted to a public status. Others may be hidden or changed and even disappear without further notice. =over 4 =item B $sh = Shell::Version->new; The constructor. =item B Shell::Perl->run_with_args; Starts the read-eval-print loop after reading options from C<@ARGV>. It is a class method. If an option B<-v> or B<--version> is provided, instead of starting the REPL, it prints the script identification and exits with 0. $ pirl -v This is pirl, version 0.0017 (bin/pirl, using Shell::Perl 0.0017) =item B $sh->run; The same as C but with no code for interpreting command-line arguments. It is an instance method, so that C<< Shell::Perl->run_with_args >> is kind of: Shell::Perl->new->run; =item B $answer = $sh->eval($exp); @answer = $sh->eval($exp); Evaluates the user input given in C<$exp> as Perl code and returns the result. That is the 'eval' part of the read-eval-print loop. =item B $sh->print(@args); Prints a list of args at the output stream currently used by the shell. =item B $sh->help; Outputs the help as provided by the command ":help". =item B $sh->reset; Does nothing by now, but it will. =item B $sh->dump_history(); $sh->dump_history($file); Prints the readline history to C or the optional file. Used to implement experimental command ":dump history". This is experimental code and should change in the future. More control should be added and integrated with other terminal features. =item B $sh->set_ctx($context); Assigns to the current shell context. The argument must be one of C< ( 'scalar', 'list', 'void', 's', 'l', 'v', '$', '@', '_' ) >. =item B $sh->set_package($package); Changes current evaluation package. Doesn't change if the new package name is malformed. =item B $sh->set_perl_version($version); Changes perl version used to evaluate statements. =item B $sh->set_out($dumper); Changes the current dumper used for printing the evaluation results. Actually must be one of "D" (for Data::Dump), "DD" (for Data::Dumper), "DDS" (for Data::Dump::Streamer), "Y" (for YAML) or "P" (for plain string interpolation). =item B $prompt = $sh->prompt_title; Returns the current prompt which changes with executable name and context. For example, "pirl @>", "pirl $>", and "pirl >". =item B $sh->quit; This method is invoked when these commands and statements are parsed by the REPL: :q :quit :x :exit quit exit It runs the shutdown procedures for a smooth termination of the shell. For example, it saves the terminal history file. =back =head1 GORY DETAILS =head2 ABOUT EVALUATION When the statement read is evaluated, this is done at a different package, which is C by default. So: $ perl -Mlib=lib bin/pirl Welcome to the Perl shell. Type ':help' for more information pirl @> $a = 2; 2 pirl @> :set out Y # output in YAML pirl @> \%Shell::Perl::sandbox:: --- BEGIN: !!perl/glob: PACKAGE: Shell::Perl::sandbox NAME: BEGIN a: !!perl/glob: PACKAGE: Shell::Perl::sandbox NAME: a SCALAR: 2 This package serves as an environment for the current shell session and :reset can wipe it away. pirl @> :reset pirl @> \%Shell::Perl::sandbox:: --- BEGIN: !!perl/glob: PACKAGE: Shell::Perl::sandbox NAME: BEGIN =head1 TO DO There is a lot to do, as always. Some of the top priority tasks are: =over 4 =item * Accept multiline statements;. =item * Refactor the code to promote easy customization of features. =back =head1 BUGS It is a one-line evaluator by now. I don't know what happens if you eval within an eval. I don't expect good things to come. (Lorn who prodded me about this will going to find it out and then I will tell you.) There are some quirks with Term::Readline (at least on Windows). There are more bugs. I am lazy to collect them all and list them now. Please report bugs via Github L. =head1 SEE ALSO This project is hosted at Github: https://github.com/aferreira/pirl To know about interactive Perl interpreters, there are two FAQS contained in L which are good starting points. Those are How can I use Perl interactively? http://perldoc.perl.org/perlfaq3.html#How-can-I-use-Perl-interactively%3f Is there a Perl shell? http://perldoc.perl.org/perlfaq3.html#How-can-I-use-Perl-interactively%3f Also: =over 4 =item * L =item * L =item * L =back =head1 AUTHORS Adriano R. Ferreira, EferreiraE<64>cpan.orgE Caio Marcelo, EcmarceloE<64>gmail.comE Ron Savage, EronE<64>savage.net.auE =head1 COPYRIGHT AND LICENSE Copyright (C) 2007–2017 by Adriano R. Ferreira This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut # Local variables: # c-indentation-style: bsd # c-basic-offset: 4 # indent-tabs-mode: nil # End: # vim: expandtab shiftwidth=4: Shell-Perl-0.0026/lib/Shell/Perl/Dumper.pm000644 000765 000024 00000020633 13034520463 021027 0ustar00ferreirastaff000000 000000 package Shell::Perl::Dumper; use strict; use warnings; our $VERSION = '0.0026'; use base qw(Class::Accessor); # to get a new() for free package Shell::Perl::Dumper::Plain; our @ISA = qw(Shell::Perl::Dumper); # to get a new() for free sub is_available { return 1; # always available - no dependency but Perl } sub dump_scalar { shift; return "$_[0]" . "\n"; } sub dump_list { shift; local $" = "\t"; return "@_" . "\n"; } package Shell::Perl::Data::Dump; our @ISA = qw(Shell::Perl::Dumper); # to get a new() for free # XXX make a Data::Dump object an instance variable sub _dump_code_filter { my ($ctx, $object_ref) = @_; return undef unless $ctx->is_code; require B::Deparse; my $code = 'sub ' . (B::Deparse->new)->coderef2text($object_ref); return { dump => $code }; } sub is_available { return eval { require Data::Dump::Filtered; 1 }; } sub dump_scalar { shift; require Data::Dump::Filtered; return Data::Dump::Filtered::dump_filtered(shift, \&_dump_code_filter) . "\n"; } sub dump_list { shift; require Data::Dump::Filtered; return Data::Dump::Filtered::dump_filtered(@_, \&_dump_code_filter) . "\n"; } package Shell::Perl::Data::Dumper; our @ISA = qw(Shell::Perl::Dumper); # XXX make a Data::Dumper object an instance variable # but OO Data::Dumper is very annoying sub is_available { return eval { require Data::Dumper; 1 }; } sub dump_scalar { shift; require Data::Dumper; local $Data::Dumper::Deparse = 1; return Data::Dumper->Dump([shift], [qw($var)]); } sub dump_list { #goto &dump_scalar if @_==2; # fallback to dump_scalar if only one shift; require Data::Dumper; local $Data::Dumper::Deparse = 1; return Data::Dumper->Dump([[@_]], [qw(*var)]); } package Shell::Perl::Dumper::YAML; our @ISA = qw(Shell::Perl::Dumper); sub _require_one_of { my @modules = @_; for (@modules) { my $ret = eval "require $_; 1"; warn "pirl: $_ loaded ok\n" if $ret; # XXX return $_ if $ret; } return undef } our $YAML_PACKAGE; sub is_available { #return eval { require YAML; 1 }; $YAML_PACKAGE = _require_one_of(qw(YAML::Syck YAML)); if ($YAML_PACKAGE) { $YAML_PACKAGE->import(qw(Dump)); do { no strict 'refs'; ${ $YAML_PACKAGE . '::DumpCode' } = 1 }; return 1 } else { return undef; } } sub dump_scalar { shift; #require YAML; # done by &is_available return Dump(shift); } sub dump_list { # XXX shift; #require YAML; # done by &is_available return Dump(@_); } package Shell::Perl::Data::Dump::Streamer; our @ISA = qw(Shell::Perl::Dumper); sub is_available { return eval { require Data::Dump::Streamer; 1 }; } sub dump_scalar { shift; require Data::Dump::Streamer; return Data::Dump::Streamer::Dump(shift)->Names('$var')->Out; } sub dump_list { #goto &dump_scalar if @_==2; # fallback to dump_scalar if only one shift; require Data::Dump::Streamer; return Data::Dump::Streamer::Dump([@_])->Names('*var')->Out; } 1; __END__ =pod =encoding utf-8 =head1 NAME Shell::Perl::Dumper - Dumpers for Shell::Perl =head1 SYNOPSYS use Shell::Perl::Dumper; $dumper = Shell::Perl::Dumper::Plain->new; print $dumper->dump_scalar($scalar); print $dumper->dump_list(@list); =head1 DESCRIPTION In C, the result of the evaluation is transformed into a string to be printed. As this result may be a pretty complex data structure, the shell provides a hook for you to pretty-print these answers just the way you want. By default, C will try to convert the results via C. That means the output will be Perl code that may be run to get the data structure again. Alternatively, the shell may use C or C with almost the same result with respect to the representation as Perl code. (But the output of the modules differ enough for sufficiently complex data.) Other options are to set the output to produce YAML or a plain simple-minded solution which basically turns the result to string via simple interpolation. All of these are implemented via I. Dumpers are meant to be used like that: $dumper = Some::Dumper::Class->new; # build a dumper $s = $dumper->dump_scalar($scalar); # from scalar to string $s = $dumper->dump_list(@list); # from list to string =head2 METHODS The following methods compose the expected API of a dumper, as used by L. =over 4 =item B $dumper = $class->new(@args); Constructs a dumper. =item B $s = $dumper->dump_scalar($scalar); Turns a scalar into a string representation. =item B $s = $dumper->dump_list(@list); Turns a list into a string representation. =item B $ok = $class->is_available This is an I class method. If it exists, it means that the class has external dependencies (like C depends on C) and whether these may be loaded when needed. If they can, this method returns true. Otherwise, returning false means that a dumper instance of this class probably cannot work. This is typically because the dependency is not installed or cannot be loaded due to an installation problem. This is the algorithm used by L XXX XXX XXX 1. =back =head1 THE STANDARD DUMPERS L provides four standard dumpers: * Shell::Perl::Data::Dump * Shell::Perl::Data::Dumper * Shell::Perl::Data::Dump::Streamer * Shell::Perl::Dumper::YAML * Shell::Perl::Dumper::Plain which corresponds to the four options of the command C< :set out >: "D", "DD", "DDS", "Y", and "P" respectively. =head2 Data::Dump The package C implements a dumper which uses L to turn Perl variables into a string representation. It is used like this: use Shell::Perl::Dumper; if (!Shell::Perl::Data::Dump->is_available) { die "the dumper cannot be loaded correctly" } $dumper = Shell::Perl::Data::Dump->new; print $dumper->dump_scalar($scalar); print $dumper->dump_list(@list); Examples of its output: pirl > :set out D pirl > { a => 3 } #scalar { a => 3 } pirl > (1, 2, "a") #list (1, 2, "a") =head2 Data::Dumper The package C implements a dumper which uses L to turn Perl variables into a string representation. It is used like this: use Shell::Perl::Dumper; if (!Shell::Perl::Data::Dumper->is_available) { die "the dumper cannot be loaded correctly" } $dumper = Shell::Perl::Data::Dumper->new; print $dumper->dump_scalar($scalar); print $dumper->dump_list(@list); Examples of its output: pirl > :set out DD pirl > { a => 3 } #scalar @var = ( { 'a' => 3 } ); pirl > (1, 2, "a") #list @var = ( 1, 2, 'a' ); =head2 YAML The package C implements a dumper which uses L or L to turn Perl variables into a string representation. It is used like this: use Shell::Perl::Dumper; if (!Shell::Perl::Dumper::YAML->is_available) { die "the dumper cannot be loaded correctly" } $dumper = Shell::Perl::Dumper::YAML->new; print $dumper->dump_scalar($scalar); print $dumper->dump_list(@list); Examples of its output: pirl > :set out Y pirl @> { a => 3 } #scalar --- a: 3 pirl @> (1, 2, "a") #list --- 1 --- 2 --- a When loading, C is preferred to C. If it is not available, the C module is the second option. =head2 Data::Dump::Streamer The documentation is yet to be written. =head2 Plain Dumper The package C implements a dumper which uses string interpolation to turn Perl variables into strings. It is used like this: use Shell::Perl::Dumper; $dumper = Shell::Perl::Dumper::Plain->new; print $dumper->dump_scalar($scalar); print $dumper->dump_list(@list); Examples of its output: pirl > :set out P pirl > { a => 3 } #scalar HASH(0x1094d2c0) pirl > (1, 2, "a") #list 1 2 a =head1 SEE ALSO See L for more documentation. =head1 COPYRIGHT AND LICENSE Copyright (C) 2007–2017 by Adriano R. Ferreira This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Shell-Perl-0.0026/bin/pirl000755 000765 000024 00000002473 13034520473 016165 0ustar00ferreirastaff000000 000000 #!/usr/bin/perl use strict; use warnings; our $VERSION = '0.0026'; use Shell::Perl (); Shell::Perl->run_with_args; __END__ =pod =encoding utf-8 =head1 NAME pirl - A read-eval-print loop in Perl (see Shell::Perl) =head1 SYNOPSIS pirl pirl --noornaments pirl --version pirl -v pirl -I dir pirl -Idir1 -Idir2 =head1 EXAMPLE SESSION $ pirl Welcome to the Perl shell. Type ':help' for more information pirl @> 1+1 2 pirl @> use YAML qw(Load Dump); () pirl @> $data = Load("--- { a: 1, b: [ 1, 2, 3] }\n"); { a => 1, b => [1, 2, 3] } pirl @> $var = 'a 1 2 3'; $var =~ /(\w+) (\d+) (\d+)/ ("a", 1, 2) pirl @> :q =head1 DESCRIPTION This script is the command-line interface to C which does it all. By now, read the fine details at C documentation. =head1 OPTIONS -I dir - add the given directory to @INC --ornaments - turn on terminal ornaments (default) --noornaments - turn off terminal organments --version, -v - prints version info and exits with 0 =head1 SEE ALSO See L for more documentation. =head1 COPYRIGHT AND LICENSE Copyright (C) 2007–2011 by Adriano R. Ferreira This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut