| Shell-Perl documentation | Contained in the Shell-Perl distribution. |
Shell::Perl - A read-eval-print loop in Perl
use Shell::Perl;
Shell::Perl->run_with_args;
This is the implementation of a command-line interpreter for Perl. I wrote this because I was tired of using irb 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 pirl script provided with Shell-Perl distribution, along with this module.
$ 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
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.
Handy for remembering what the shell commands are.
Leave the shell. The Perl statement exit will work too.
SYNONYMS: :exit, :x
Changes the dumper for the expression results used before output. The current supported are:
Data::Dump
Data::Dumper, the good and old core module
Data::Dump::Streamer
YAML
a plain dumper ("$ans" or "@ans")
When creating the shell, the dump format is searched among the available ones in the order "D", "DD", "DDS", "Y" and "P". That means Data::Dump is preferred and will be used if available/installed. Otherwise, Data::Dumper is tried, and so on.
Read more about dumpers at Shell::Perl::Dumper.
Changes the default context used to evaluate the entered expression.
The default is '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
Resets the environment, erasing the symbols created at the current evaluation package. See the section "ABOUT EVALUATION".
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.
$sh = Shell::Version->new;
The constructor.
Shell::Perl->run_with_args;
Starts the read-eval-print loop after reading
options from @ARGV. It is a class method.
If an option -v or --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)
$sh->run;
The same as run_with_args but with no code for
interpreting command-line arguments. It is an instance method,
so that Shell::Perl-run_with_args> is kind of:
Shell::Perl->new->run;
$answer = $sh->eval($exp);
@answer = $sh->eval($exp);
Evaluates the user input given in $exp as Perl code and returns
the result. That is the 'eval' part of the
read-eval-print loop.
$sh->print(@args);
Prints a list of args at the output stream currently used by the shell.
$sh->help;
Outputs the help as provided by the command ":help".
$sh->reset;
Does nothing by now, but it will.
$sh->dump_history();
$sh->dump_history($file);
Prints the readline history to STDOUT 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.
$sh->set_ctx($context);
Assigns to the current shell context. The argument
must be one of ( 'scalar', 'list', 'void',
's', 'l', 'v', '$', '@', '_' ) .
$sh->set_package($package);
Changes current evaluation package. Doesn't change if the new package name is malformed.
$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).
$prompt = $sh->prompt_title;
Returns the current prompt which changes with executable name and context. For example, "pirl @>", "pirl $>", and "pirl >".
$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.
When the statement read is evaluated, this is done
at a different package, which is Shell::Perl::sandbox
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
There is a lot to do, as always. Some of the top priority tasks are:
This project is hosted at Google Code:
http://code.google.com/p/iperl/
To know about interactive Perl interpreters, there are two FAQS contained in perlfaq3 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
An extra list of Perl shells can be found here:
http://www.focusresearch.com/gregor/document/psh-1.1.html#other_perl_shells
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 CPAN RT http://rt.cpan.org/NoAuth/Bugs.html?Dist=Shell-Perl or mailto://bugs-Shell-Perl@rt.cpan.org.
Adriano R. Ferreira, <ferreira@cpan.org>
Caio Marcelo, <cmarcelo@gmail.com>
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.
| Shell-Perl documentation | Contained in the Shell-Perl distribution. |
package Shell::Perl; use 5.006; use strict; use warnings; # /Id: Perl.pm 1131 2007-01-27 17:43:35Z me / # don't erase that for now # $Id: /iperl/lib/Shell/Perl.pm 2317 2008-03-09T16:22:00.577930Z a.r.ferreira@gmail.com $ our $VERSION = '0.0022'; use base qw(Class::Accessor); # soon use base qw(Shell::Base); Shell::Perl->mk_accessors(qw( out_type dumper context package term ornaments )); # XXX use_strict 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 @_ }); $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 } $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
sub _print_scalar { # XXX make public, document my $self = shift; $self->print($self->dumper->dump_scalar(shift)); }
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"); } } 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 <name> - set package in which shell eval statements :reset - reset the environment :dump history <file> - (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' )->stringify; } 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 ) { require File::Slurp; my @h = File::Slurp::read_file( $h ); chomp @h; $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} ) { require File::Slurp; my @h = map { "$_\n" } $term->GetHistory; File::Slurp::write_file( $h, @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->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; } # $shell->eval($exp) sub eval { my $self = shift; my $exp = shift; my $package = $self->package; # XXX gotta restore $_, etc. return eval <<CHUNK; package $package; # XXX no strict qw(vars subs); #line 1 $exp CHUNK # XXX gotta save $_, etc. } sub quit { my $self = shift; _write_history( $self->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' ); } 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 { ? } # svn:keywords Id # svn:eol-style LF __END__
# Local variables: # c-indentation-style: bsd # c-basic-offset: 4 # indent-tabs-mode: nil # End: # vim: expandtab shiftwidth=4: