From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/env perl
#
# Copyright (c) 2002,2014-2015,2020 Christian Jaeger, copying@christianjaeger.ch
#
# This is free software, offered under either the same terms as perl 5
# or the terms of the Artistic License version 2 or the terms of the
# MIT License (Expat version). See the file COPYING.md that came
# bundled with this file.
#
use strict;
use warnings FATAL => 'uninitialized';
use Cwd 'abs_path';
our ($mydir, $myname);
BEGIN {
my $location = (-l $0) ? abs_path($0) : $0;
$location =~ /(.*?)([^\/]+?)_?\z/s or die "?";
($mydir, $myname) = ($1, $2);
}
use lib "$mydir/../lib";
sub usage {
print "$myname [ calculation(s) ]
Repl-Enhanced PerL
Evaluates the given string(s), or goes into interactive mode if
none given.
Interactive mode tricks:
- uses readline (read up on its features)
- entering nothing re-evaluates the last entry
- 'strict vars' is not active, so there's no need to declare new
globals with 'our'
- global variables and subroutines stay around, lexical ('my')
varibles don't
- you can refer to '\$VAR1' etc. to get the correspondin result from the
previous evaluation (as long as you're using the default ':d'
display mode)
- enter ':?' to get a list of special commands and mode switches
Options:
-t trap exceptions in a repl, too
-H|--no-history do not save history to file (it is still read on
startup.)
--name name use name instead of the name of this script as part
of the history file name: ~/.\${name}_history
-M module use the given module (namespace or path)
-m module same as -M but don't complain about failures
";
exit @_;
}
use Chj::xhome qw(xsafehome);
my @files;
my $opt_H;
my @opt_M;
my @opt_m;
my $opt_t;
for (my $i = 0; $i <= $#ARGV; $i++) {
local $_ = $ARGV[$i];
if (/^--?h(elp)?$/) {
usage
} elsif ($_ eq '--') {
push @files, @ARGV[$i + 1 .. $#ARGV];
last;
} elsif ($_ eq '-H' or $_ eq '--no-history') {
$opt_H = 1;
} elsif ($_ eq '-t') {
$opt_t = 1;
} elsif (/^-M(.*)/) {
if ($1) {
push @opt_M, $1;
} else {
my $arg = $ARGV[++$i];
defined $arg or usage "missing argument to -M";
push @opt_M, $arg;
}
} elsif (/^-m(.*)/) {
if ($1) {
push @opt_m, $1;
} else {
my $arg = $ARGV[++$i];
defined $arg or usage "missing argument to -m";
push @opt_m, $arg;
}
} elsif (/^--name$/) {
my $arg = $ARGV[++$i];
defined $arg or usage "missing argument to --name";
$myname = $arg;
} elsif (/^-/) {
warn "Unknown option '$_'\n";
usage(1)
} else {
push @files, $_
}
}
my $HISTFILE = xsafehome . "/.${myname}_history";
#my $MAXHISTLEN = 500;
eval { require Chj::BinHexOctDec; };
warn $@ if $@;
use Chj::Package::OfPath qw(package_of_path_or_package);
use FP::Repl::WithRepl qw(push_withrepl WithRepl_eval WithRepl_eval_e);
push_withrepl(0) if $opt_t;
sub moduleoption_to_code {
@_ == 1 or fp_croak_arity 1;
my ($a) = @_;
$a =~ s/([^=]+)//s or die "invalid -M option '$_'";
my $path_or_package = $1;
my $p = package_of_path_or_package($path_or_package);
"use " . do {
if ($a =~ s/^=//s) {
$p . " " . join(", ", map {"'$_'"} split /,/, $a)
} else {
$p
}
}
}
if (@opt_M) {
my $code = join(";", map { moduleoption_to_code $_ } @opt_M);
&WithRepl_eval("package $myname; " . $code . "; 1") or die $@;
}
for my $path_or_package (@opt_m) {
eval moduleoption_to_code $path_or_package;
}
if (@files) {
for (@files) {
my ($res, $e, $is_error) = &WithRepl_eval_e($_, $myname);
die $e if $is_error;
print "$res\n";
}
} else {
require FP::Repl;
FP::Repl::repl(
maybe_historypath => ($opt_H ? undef : $HISTFILE),
#maxhistlen => $MAXHISTLEN,
maybe_package => $myname
);
}