#!/usr/bin/perl # # by Erik Osheim # # licensed under the GNU GPL version 2 # try not to pollute the namespace use Data::Dumper qw(Dumper); use File::Basename qw(); use Getopt::Long qw(); use Perl6::Slurp qw(slurp); use Scalar::Util qw(); use Term::ReadLine qw(); # globals Getopt::Long::Configure('bundling'); $| = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 1; my $PIPE; # usage function sub usage { my($status) = @_; my $prog = File::Basename::basename($0); print < 1} unless $proc; $pkg = $base if $base; my @results; foreach my $item (eval("keys(\%${pkg})")) { my $item2 = $base . $item; next if $proc->{$item2}; if($item =~ m/$nameregex/) { push(@results, $item2) if eval("defined(${sigil}::${base}${item})"); } elsif($item =~ m/$pkgregex/) { $proc->{$item2} = 1; push(@results, sigilfind($item2, $sigil, $item2, $proc)); } } return @results; } # handy function for printing nice representations of data sub repr { my ($item) = @_; if(!defined($item)) { return 'undef'; } elsif(Scalar::Util::blessed($item)) { return "$item"; } elsif(ref($item) eq 'CODE') { return "$item"; } else { my $s = Dumper($item); $s =~ s/\n+$//; return $s; } } # use sigilfind to get completions for particular word(s) sub complete { my ($word, $line, $x) = @_; $word =~ m/^([&\$%@\*]?)(.*)$/; my $sigil = $1 ? $1 : '&'; my $name = $2; my @candidates = grep { $_ =~ m/^$name/ } sigilfind('main::', $sigil); 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 = < 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) { if($pair->[0] eq 'eval') { eval $pair->[1]; die "failed to eval '$pair->[1]': $@" if $@; } elsif($pair->[0] eq 'run') { die "no path named $pair->[1] found" unless -e $pair->[1]; my $data = slurp($pair->[1]); eval $data; die "failed to run $pair->[1]: $@" if $@; } elsif($pair->[0] eq 'use') { eval "use $pair->[1]"; die "failed to use $pair->[1]: $@" if $@; } } # set up readline if necessary unless($pipe) { $term = Term::ReadLine->new('IPERL'); my $attribs = $term->Attribs(); $attribs->{special_prefixes} = '$@%*&'; $attribs->{completion_function} = sub { return complete(@_) }; } # the mighty read-exec-print loop! while(1) { # display the prompt and read some input my $line; if($pipe) { print "PROMPT:$prompt\n"; $line = ; } else { $line = $term->readline("$prompt "); } last unless defined($line); chomp($line); # if we're in pipe-mode then we expect input in a special form if($pipe) { if($line =~ m/ENTER:(.*)$/) { $line = $1; } elsif($line =~ m/COMPLETE:(.*)$/) { draw_completions(complete($1)); next; } else { print "malformed pipe input line\n"; next; } } # process the line of input if($line eq '') { } elsif($line eq ':exit' || $line eq ':quit') { last; } elsif($line eq ':help') { ($input, $prompt) = ("", ">>>"); print $HELP; } elsif($line eq ':reload') { ($input, $prompt) = ("", ">>>"); print "reloading...\n"; exec($0, @oldargv); } elsif($line eq ':sh') { ($input, $prompt) = ("", ">>>"); system("bash") unless $pipe; } elsif($line =~ m/^:sh (.+)$/) { ($input, $prompt) = ("", ">>>"); system($1) unless $pipe; } elsif($line eq ':code') { draw_completions(sigilfind('main::', '&')); } elsif($line eq ':scalar') { draw_completions(sigilfind('main::', '$')); } elsif($line eq ':hash') { draw_completions(sigilfind('main::', '%')); } elsif($line eq ':array') { draw_completions(sigilfind('main::', '@')); } elsif($line eq ':glob') { draw_completions(sigilfind('main::', '*')); } elsif($line =~ m/^[\t ]/ || $line =~ m/\{ *$/ || m/\\ *$/) { # we're dealing with an incomplete statement, so defer execution $line =~ s/\\ *$//; $input .= $line; $prompt = "..>"; } else { # we're dealing with a complete statement, so execute and display $input .= $line; my @results = map { repr($_) } eval($input); if($@) { print $@; } elsif(scalar(@results) == 0) { } elsif(scalar(@results) == 1) { print $results[0] . "\n" unless $results[0] eq 'undef'; } else { print join(", ", @results) . "\n"; } ($input, $prompt) = ("", ">>>"); } } print "Bye.\n"; } main();