branch : pmacs2
This commit is contained in:
moculus 2008-05-16 20:27:03 +00:00
parent 31327aae83
commit b725b91375
1 changed files with 64 additions and 82 deletions

View File

@ -12,12 +12,14 @@ use Perl6::Slurp qw(slurp);
use Scalar::Util qw(); use Scalar::Util qw();
use Term::ReadLine qw(); use Term::ReadLine qw();
# globals
Getopt::Long::Configure('bundling'); Getopt::Long::Configure('bundling');
$| = 1; $| = 1;
$Data::Dumper::Terse = 1; $Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 1; $Data::Dumper::Indent = 1;
my $PIPE;
# usage function
sub usage { sub usage {
my($status) = @_; my($status) = @_;
my $prog = File::Basename::basename($0); my $prog = File::Basename::basename($0);
@ -26,35 +28,14 @@ usage: $prog [options]
-h,--help show this message -h,--help show this message
-e,--eval STR eval STR before interaction -e,--eval STR eval STR before interaction
-p,--pipe run in pipe mode (as a subprocess) -p,--pipe run in pipe mode (as a subprocess)
-q,--quiet do not output eval results
-r,--run PATH run PATH before interaction -r,--run PATH run PATH before interaction
-v,--verbose output eval results (default)
perl needs a good interpreter. perl needs a good interpreter.
EOT EOT
exit($status); exit($status);
} }
my @OLDARGV = @ARGV; # handy function for inspecting the symbol table
my $verbose = 'yes';
my $pipe;
my @preload;
Getopt::Long::GetOptions(
'help|h' => sub { usage(0); },
'eval|e=s' => sub { push(@preload, ['eval', $_[1]]); },
'pipe|p' => sub { $pipe = 'line' },
'quiet|q' => sub { $verbose = 0 },
'run|r=s' => sub { push(@preload, ['run', $_[1]]); },
'use|u=s' => sub { push(@preload, ['use', $_[1]]); },
'verbose|v' => sub { $verbose = 'please'; },
) || usage(1);
my ($prompt, $input) = (">>>", "");
unless($pipe) {
print "Welcome to Erik's Perl Interpreter\n";
print "(Type \"help\", \"exit\", or something else)\n";
}
my $pkgregex = qr/^(?:[a-zA-Z_][a-zA-Z0-9_]*::)+$/; my $pkgregex = qr/^(?:[a-zA-Z_][a-zA-Z0-9_]*::)+$/;
my $nameregex = qr/^[a-zA-Z_][a-zA-Z0-9_]*$/; my $nameregex = qr/^[a-zA-Z_][a-zA-Z0-9_]*$/;
sub sigilfind { sub sigilfind {
@ -76,7 +57,8 @@ sub sigilfind {
return @results; return @results;
} }
sub _repr { # handy function for printing nice representations of data
sub repr {
my ($item) = @_; my ($item) = @_;
if(!defined($item)) { if(!defined($item)) {
return 'undef'; return 'undef';
@ -91,36 +73,9 @@ sub _repr {
} }
} }
sub repr { # use sigilfind to get completions for particular word(s)
my @output;
foreach my $item (@_) {
push(@output, _repr($item));
}
return join("\n", @output);
}
sub escape {
my($s) = @_;
$s =~ s/\\/\\\\/g;
$s =~ s/\n/\\n/g;
return $s;
}
sub draw_prompt {
my($p) = @_;
print $pipe ? "$prefix:" . escape($p) . "\n" : $p;
}
sub draw_completions {
my (@items) = @_;
if($pipe) {
print 'COMPLETIONS:' . join('|', @items) . "\n";
} else {
print join("\n", @items) . "\n";
}
}
sub complete { sub complete {
my ($word) = @_; my ($word, $line, $x) = @_;
$word =~ m/^([&\$%@\*]?)(.*)$/; $word =~ m/^([&\$%@\*]?)(.*)$/;
my $sigil = $1 ? $1 : '&'; my $sigil = $1 ? $1 : '&';
my $name = $2; my $name = $2;
@ -130,24 +85,51 @@ sub complete {
return map { "${1}$_" } sort(@candidates); return map { "${1}$_" } sort(@candidates);
} }
# display completions to the user
sub draw_completions {
my (@items) = @_;
my($prefix, $delim) = $pipe ? ("COMPLETIONS", "|") : ("", "\n");
print $prefix . join($delim, @items) . "\n";
}
# some help text
my $HELP = <<EOT; my $HELP = <<EOT;
Greetings! Greetings!
I'm program is designed to help you write Perl. Like the "perl" command, I'm program is designed to help you write Perl. Like the "perl" command,
I can parse and execute Perl code. Unlike it, I can execute perl code I can parse and execute Perl code. Unlike it, I can execute perl code
interactively, allowing you to call functions, save their results, inspect interactively, allowing you to call functions, save their results, inspect
the results, and repeat. the results, and repeat. I also support tab-completion!
I contain a lot of tricky functionality. I contain a lot of tricky functionality.
One side-note: I can't be run under the "strict" pragma; thus, you don't One side-note: I can't be run under the "strict" pragma; thus, you don't
need to use my() or our() to declare variables. The reaon for this has to need to use my() or our() to declare variables. The reaon for this has to
do with the way I evaluate blocks you enter. Sorry. do with the way I evaluate blocks. Sorry.
EOT EOT
my $term; # the big one!
sub main { sub main {
# process the arguments provided; save original @ARGV for use later
my @preload;
my @oldargv = @ARGV;
Getopt::Long::GetOptions(
'help|h' => sub { usage(0); },
'eval|e=s' => sub { push(@preload, ['eval', $_[1]]); },
'pipe|p' => sub { $pipe = 'line' },
'run|r=s' => sub { push(@preload, ['run', $_[1]]); },
'use|u=s' => sub { push(@preload, ['use', $_[1]]); },
) || usage(1);
# let's display a nice banner to the user
my ($prompt, $input, $term) = (">>>", "", undef);
unless($pipe) {
print "Welcome to Erik's Perl Interpreter\n";
print "(Type \":help\", \":exit\", or something else)\n";
}
# for every file or perl string we were given, eval them in order; we should
# die if any of them fail to load.
foreach my $pair (@preload) { foreach my $pair (@preload) {
if($pair->[0] eq 'eval') { if($pair->[0] eq 'eval') {
eval $pair->[1]; eval $pair->[1];
@ -163,20 +145,20 @@ sub main {
} }
} }
# set up readline if necessary
unless($pipe) { unless($pipe) {
$term = Term::ReadLine->new('IPERL'); $term = Term::ReadLine->new('IPERL');
my $attribs = $term->Attribs(); my $attribs = $term->Attribs();
$attribs->{special_prefixes} = '$@%*&'; $attribs->{special_prefixes} = '$@%*&';
$attribs->{completion_function} = sub { $attribs->{completion_function} = sub { return complete(@_) };
my ($word, $line, $x) = @_;
return complete($word);
};
} }
# the mighty read-exec-print loop!
while(1) { while(1) {
# display the prompt and read some input
my $line; my $line;
if($pipe) { if($pipe) {
draw_prompt($prompt); print "PROMPT:$prompt\n";
$line = <STDIN>; $line = <STDIN>;
} else { } else {
$line = $term->readline("$prompt "); $line = $term->readline("$prompt ");
@ -184,36 +166,36 @@ sub main {
last unless defined($line); last unless defined($line);
chomp($line); chomp($line);
# if we're in pipe-mode then we expect input in a special form
if($pipe) { if($pipe) {
if($line =~ m/ENTER:(.*)$/) { if($line =~ m/ENTER:(.*)$/) {
$line = $1; $line = $1;
} elsif($line =~ m/COMPLETE:(.*)$/) {
draw_completions(complete($1));
next;
} else { } else {
if($line =~ m/COMPLETE:(.*)$/) { print "malformed pipe input line\n";
draw_completions(complete($1));
} else {
print "malformed pipe input line\n";
}
next; next;
} }
} }
# process the line of input
if($line eq '') { if($line eq '') {
} elsif($line eq 'exit') { } elsif($line eq ':exit' || $line eq ':quit') {
last; last;
} elsif($line eq 'help') { } elsif($line eq ':help') {
($input, $prompt) = ("", ">>>"); ($input, $prompt) = ("", ">>>");
print $HELP; print $HELP;
} elsif($line eq 'reload') { } elsif($line eq ':reload') {
($input, $prompt) = ("", ">>>"); ($input, $prompt) = ("", ">>>");
print "reloading...\n"; print "reloading...\n";
exec($0, @OLDARGV); exec($0, @oldargv);
} elsif($line eq 'sh') { } elsif($line eq ':sh') {
($input, $prompt) = ("", ">>>"); ($input, $prompt) = ("", ">>>");
system("bash") unless $pipe; system("bash") unless $pipe;
} elsif($line =~ m/^sh (.+)$/) { } elsif($line =~ m/^:sh (.+)$/) {
($input, $prompt) = ("", ">>>"); ($input, $prompt) = ("", ">>>");
system($1) unless $pipe; system($1) unless $pipe;
} elsif($line =~ m/^:tab *(.*)$/) {
} elsif($line eq ':code') { } elsif($line eq ':code') {
draw_completions(sigilfind('main::', '&')); draw_completions(sigilfind('main::', '&'));
} elsif($line eq ':scalar') { } elsif($line eq ':scalar') {
@ -224,20 +206,20 @@ sub main {
draw_completions(sigilfind('main::', '@')); draw_completions(sigilfind('main::', '@'));
} elsif($line eq ':glob') { } elsif($line eq ':glob') {
draw_completions(sigilfind('main::', '*')); draw_completions(sigilfind('main::', '*'));
} elsif($line =~ m/^[\t ]/ || $line =~ m/\{ *$/) { } elsif($line =~ m/^[\t ]/ || $line =~ m/\{ *$/ || m/\\ *$/) {
$input .= $line; # we're dealing with an incomplete statement, so defer execution
$prompt = "..>";
} elsif($line =~ m/\\ *$/) {
$line =~ s/\\ *$//; $line =~ s/\\ *$//;
$input .= $line; $input .= $line;
$prompt = "..>"; $prompt = "..>";
} else { } else {
# we're dealing with a complete statement, so execute and display
$input .= $line; $input .= $line;
my @results = map { $_ =~ s/\n$//; repr($_); } eval($input); my @results = map { repr($_) } eval($input);
if($@) { if($@) {
print $@; print $@;
} elsif(scalar(@results) < 2) { } elsif(scalar(@results) == 0) {
print $results[0] . "\n"; } elsif(scalar(@results) == 1) {
print $results[0] . "\n" unless $results[0] eq 'undef';
} else { } else {
print join(", ", @results) . "\n"; print join(", ", @results) . "\n";
} }