Commit | Line | Data |
afe61f9c |
1 | package Devel::REPL; |
9d2a4940 |
2 | # ABSTRACT: A modern perl interactive shell |
afe61f9c |
3 | |
c45376f3 |
4 | our $VERSION = '1.003029'; |
54beb05d |
5 | |
afe61f9c |
6 | use Term::ReadLine; |
7 | use Moose; |
aa8b7647 |
8 | use namespace::autoclean; |
089a0c4e |
9 | use 5.008001; # backwards compat, doesn't warn like 5.8.1 |
59aedffc |
10 | |
afe61f9c |
11 | with 'MooseX::Object::Pluggable'; |
12 | |
e22aa835 |
13 | use Devel::REPL::Error; |
14 | |
afe61f9c |
15 | has 'term' => ( |
b595a818 |
16 | is => 'rw', |
2df2b90e |
17 | lazy => 1, |
afe61f9c |
18 | default => sub { Term::ReadLine->new('Perl REPL') } |
19 | ); |
20 | |
21 | has 'prompt' => ( |
b595a818 |
22 | is => 'rw', |
afe61f9c |
23 | default => sub { '$ ' } |
24 | ); |
25 | |
26 | has 'out_fh' => ( |
b595a818 |
27 | is => 'rw', |
28 | lazy => 1, |
afe61f9c |
29 | default => sub { shift->term->OUT || \*STDOUT; } |
30 | ); |
31 | |
57719095 |
32 | has 'exit_repl' => ( |
b595a818 |
33 | is => 'rw', |
57719095 |
34 | default => sub { 0 } |
35 | ); |
36 | |
afe61f9c |
37 | sub run { |
38 | my ($self) = @_; |
e22aa835 |
39 | while ($self->run_once_safely) { |
57719095 |
40 | # keep looping unless we want to exit REPL |
41 | last if $self->exit_repl; |
afe61f9c |
42 | } |
43 | } |
44 | |
e22aa835 |
45 | sub run_once_safely { |
46 | my ($self, @args) = @_; |
47 | |
48 | my $ret = eval { $self->run_once(@args) }; |
49 | |
50 | if ($@) { |
51 | my $error = $@; |
52 | eval { $self->print("Error! - $error\n"); }; |
53 | return 1; |
54 | } else { |
55 | return $ret; |
56 | } |
57 | } |
58 | |
afe61f9c |
59 | sub run_once { |
60 | my ($self) = @_; |
e22aa835 |
61 | |
afe61f9c |
62 | my $line = $self->read; |
57719095 |
63 | return unless defined($line); # undefined value == EOF |
e22aa835 |
64 | |
65 | my @ret = $self->formatted_eval($line); |
66 | |
57719095 |
67 | $self->print(@ret) unless $self->exit_repl; |
e22aa835 |
68 | |
afe61f9c |
69 | return 1; |
70 | } |
71 | |
e22aa835 |
72 | sub formatted_eval { |
73 | my ( $self, @args ) = @_; |
74 | |
75 | my @ret = $self->eval(@args); |
76 | |
77 | return $self->format(@ret); |
78 | } |
79 | |
80 | sub format { |
81 | my ( $self, @stuff ) = @_; |
82 | |
c3bbf326 |
83 | if ( $self->is_error($stuff[0]) ) { |
e22aa835 |
84 | return $self->format_error(@stuff); |
85 | } else { |
86 | return $self->format_result(@stuff); |
87 | } |
88 | } |
89 | |
90 | sub format_result { |
91 | my ( $self, @stuff ) = @_; |
92 | |
93 | return @stuff; |
94 | } |
95 | |
96 | sub format_error { |
97 | my ( $self, $error ) = @_; |
98 | return $error->stringify; |
99 | } |
100 | |
c3bbf326 |
101 | sub is_error { |
102 | my ( $self, $thingy ) = @_; |
103 | blessed($thingy) and $thingy->isa("Devel::REPL::Error"); |
104 | } |
105 | |
afe61f9c |
106 | sub read { |
107 | my ($self) = @_; |
108 | return $self->term->readline($self->prompt); |
109 | } |
110 | |
911a1c24 |
111 | sub eval { |
112 | my ($self, $line) = @_; |
c3bbf326 |
113 | my $compiled = $self->compile($line); |
114 | return $compiled unless defined($compiled) and not $self->is_error($compiled); |
115 | return $self->execute($compiled); |
911a1c24 |
116 | } |
117 | |
118 | sub compile { |
e22aa835 |
119 | my ( $_REPL, @args ) = @_; |
120 | my $compiled = eval $_REPL->wrap_as_sub(@args); |
c3bbf326 |
121 | return $_REPL->error_return("Compile error", $@) if $@; |
911a1c24 |
122 | return $compiled; |
123 | } |
124 | |
125 | sub wrap_as_sub { |
e22aa835 |
126 | my ($self, $line, %args) = @_; |
127 | return qq!sub {\n!. ( $args{no_mangling} ? $line : $self->mangle_line($line) ).qq!\n}\n!; |
911a1c24 |
128 | } |
129 | |
130 | sub mangle_line { |
131 | my ($self, $line) = @_; |
132 | return $line; |
133 | } |
134 | |
afe61f9c |
135 | sub execute { |
48ddfeae |
136 | my ($self, $to_exec, @args) = @_; |
137 | my @ret = eval { $to_exec->(@args) }; |
138 | return $self->error_return("Runtime error", $@) if $@; |
afe61f9c |
139 | return @ret; |
140 | } |
141 | |
911a1c24 |
142 | sub error_return { |
143 | my ($self, $type, $error) = @_; |
e22aa835 |
144 | return Devel::REPL::Error->new( type => $type, message => $error ); |
911a1c24 |
145 | } |
146 | |
afe61f9c |
147 | sub print { |
148 | my ($self, @ret) = @_; |
149 | my $fh = $self->out_fh; |
59aedffc |
150 | no warnings 'uninitialized'; |
afe61f9c |
151 | print $fh "@ret"; |
a66625d6 |
152 | print $fh "\n" if $self->term->ReadLine =~ /Gnu/; |
afe61f9c |
153 | } |
154 | |
9d2a4940 |
155 | 1; |
156 | __END__ |
59aedffc |
157 | |
9d2a4940 |
158 | =pod |
59aedffc |
159 | |
160 | =head1 SYNOPSIS |
161 | |
162 | my $repl = Devel::REPL->new; |
163 | $repl->load_plugin($_) for qw(History LexEnv); |
164 | $repl->run |
165 | |
166 | Alternatively, use the 're.pl' script installed with the distribution |
167 | |
950232b2 |
168 | system$ re.pl |
169 | |
408564af |
170 | =head1 DESCRIPTION |
171 | |
172 | This is an interactive shell for Perl, commonly known as a REPL - Read, |
173 | Evaluate, Print, Loop. The shell provides for rapid development or testing |
174 | of code without the need to create a temporary source code file. |
175 | |
176 | Through a plugin system, many features are available on demand. You can also |
177 | tailor the environment through the use of profiles and run control files, for |
178 | example to pre-load certain Perl modules when working on a particular project. |
179 | |
180 | =head1 USAGE |
181 | |
182 | To start a shell, follow one of the examples in the L</"SYNOPSIS"> above. |
183 | |
184 | Once running, the shell accepts and will attempt to execute any code given. If |
185 | the code executes successfully you'll be shown the result, otherwise an error |
186 | message will be returned. Here are a few examples: |
187 | |
188 | $_ print "Hello, world!\n" |
189 | Hello, world! |
190 | 1 |
191 | $_ nosuchfunction |
192 | Compile error: Bareword "nosuchfunction" not allowed while "strict subs" in use at (eval 130) line 5. |
20d9434d |
193 | |
194 | $_ |
408564af |
195 | |
196 | In the first example above you see the output of the command (C<Hello, |
197 | world!>), if any, and then the return value of the statement (C<1>). Following |
198 | that example, an error is returned when the execution of some code fails. |
199 | |
200 | Note that the lack of semicolon on the end is not a mistake - the code is |
201 | run inside a Block structure (to protect the REPL in case the code blows up), |
202 | which means a single statement doesn't require the semicolon. You can add one |
203 | if you like, though. |
204 | |
6aa58492 |
205 | If you followed the first example in the L</"SYNOPSIS"> above, you'll have the |
8d5343b5 |
206 | L<History|Devel::REPL::Plugin::History> and L<LexEnv|Devel::REPL::Plugin::LexEnv> |
207 | plugins loaded (and there are many more available). |
408564af |
208 | Although the shell might support "up-arrow" history, the History plugin adds |
209 | "bang" history to that so you can re-execute chosen commands (with e.g. |
210 | C<!53>). The LexEnv plugin ensures that lexical variables declared with the |
211 | C<my> keyword will automatically persist between statements executed in the |
212 | REPL shell. |
213 | |
214 | When you C<use> any Perl module, the C<import()> will work as expected - the |
215 | exported functions from that module are available for immediate use: |
216 | |
217 | $_ carp "I'm dieeeing!\n" |
218 | String found where operator expected at (eval 129) line 5, near "carp "I'm dieeeing!\n"" |
219 | (Do you need to predeclare carp?) |
220 | Compile error: syntax error at (eval 129) line 5, near "carp "I'm dieeeing!\n"" |
221 | BEGIN not safe after errors--compilation aborted at (eval 129) line 5. |
20d9434d |
222 | |
223 | $_ use Carp |
224 | |
408564af |
225 | $_ carp "I'm dieeeing!\n" |
226 | I'm dieeeing! |
227 | at /usr/share/perl5/Lexical/Persistence.pm line 327 |
228 | 1 |
20d9434d |
229 | $_ |
408564af |
230 | |
73d11b24 |
231 | To quit from the shell, hit C<Ctrl+D> or C<Ctrl+C>. |
232 | |
233 | MSWin32 NOTE: control keys won't work if TERM=dumb |
234 | because readline functionality will be disabled. |
235 | |
408564af |
236 | |
237 | =head2 Run Control Files |
238 | |
239 | For particular projects you might well end up running the same commands each |
240 | time the REPL shell starts up - loading Perl modules, setting configuration, |
241 | and so on. A run control file lets you have this done automatically, and you |
242 | can have multiple files for different projects. |
243 | |
244 | By default the C<re.pl> program looks for C<< $HOME/.re.pl/repl.rc >>, and |
245 | runs whatever code is in there as if you had entered it at the REPL shell |
246 | yourself. |
247 | |
248 | To set a new run control file that's also in that directory, pass it as a |
249 | filename like so: |
250 | |
251 | system$ re.pl --rcfile myproject.pc |
252 | |
0e0d2539 |
253 | If the filename happens to contain a forward slash, then it's used absolutely, |
408564af |
254 | or realive to the current working directory: |
255 | |
256 | system$ re.pl --rcfile /path/to/my/project/repl.rc |
257 | |
258 | Within the run control file you might want to load plugins. This is covered in |
259 | L</"The REPL shell object"> section, below. |
260 | |
261 | =head2 Profiles |
262 | |
263 | To allow for the sharing of run control files, you can fashion them into a |
264 | Perl module for distribution (perhaps via the CPAN). For more information on |
265 | this feature, please see the L<Devel::REPL::Profile> manual page. |
266 | |
e72070d7 |
267 | A C<Standard> profile ships with C<Devel::REPL>; it loads the following plugins |
268 | (note that some of these require optional features -- or you can also use the |
269 | C<Minimal> profile): |
408564af |
270 | |
f4d48832 |
271 | =for :list |
272 | * L<Devel::REPL::Plugin::History> |
273 | * L<Devel::REPL::Plugin::LexEnv> |
274 | * L<Devel::REPL::Plugin::DDS> |
275 | * L<Devel::REPL::Plugin::Packages> |
276 | * L<Devel::REPL::Plugin::Commands> |
277 | * L<Devel::REPL::Plugin::MultiLine::PPI> |
278 | * L<Devel::REPL::Plugin::Colors> |
279 | * L<Devel::REPL::Plugin::Completion> |
280 | * L<Devel::REPL::Plugin::CompletionDriver::INC> |
281 | * L<Devel::REPL::Plugin::CompletionDriver::LexEnv> |
282 | * L<Devel::REPL::Plugin::CompletionDriver::Keywords> |
283 | * L<Devel::REPL::Plugin::CompletionDriver::Methods> |
284 | * L<Devel::REPL::Plugin::ReadlineHistory> |
408564af |
285 | |
286 | =head2 Plugins |
287 | |
0e0d2539 |
288 | Plugins are a way to add functionality to the REPL shell, and take advantage of |
408564af |
289 | C<Devel::REPL> being based on the L<Moose> object system for Perl 5. This |
290 | means it's simple to 'hook into' many steps of the R-E-P-L process. Plugins |
291 | can change the way commands are interpreted, or the way their results are |
292 | output, or even add commands to the shell environment. |
293 | |
294 | A number of plugins ship with C<Devel::REPL>, and more are available on the |
295 | CPAN. Some of the shipped plugins are loaded in the default profile, mentioned |
8d5343b5 |
296 | above. These plugins can be loaded in your F< $HOME/.re.pl/repl.rc > like: |
cfb85b27 |
297 | |
298 | load_plugin qw( CompletionDriver::Global DumpHistory ); |
408564af |
299 | |
300 | Writing your own plugins is not difficult, and is discussed in the |
301 | L<Devel::REPL::Plugin> manual page, along with links to the manual pages of |
302 | all the plugins shipped with C<Devel::REPL>. |
303 | |
304 | =head2 The REPL shell object |
305 | |
306 | From time to time you'll want to interact with or manipulate the |
307 | C<Devel::REPL> shell object itself; that is, the instance of the shell you're |
308 | currently running. |
309 | |
310 | The object is always available through the C<$_REPL> variable. One common |
311 | requirement is to load an additional plugin, after your profile and run |
312 | control files have already been executed: |
313 | |
314 | $_ $_REPL->load_plugin('Timing'); |
315 | 1 |
316 | $_ print "Hello again, world!\n" |
317 | Hello again, world! |
318 | Took 0.00148296356201172 seconds. |
319 | 1 |
320 | $_ |
321 | |
e72070d7 |
322 | =head1 OPTIONAL FEATURES |
408564af |
323 | |
e72070d7 |
324 | In addition to the prerequisites declared in this distribution, which should be automatically installed by your L<CPAN> client, there are a number of optional features, used by |
325 | additional plugins. You can install any of these features by installing this |
326 | distribution interactively (e.g. C<cpanm --interactive Devel::REPL>). |
408564af |
327 | |
e72070d7 |
328 | =for comment I hope to automatically generate this data via a Pod::Weaver section |
ab213f1f |
329 | |
f4d48832 |
330 | =for :list |
331 | * Completion plugin - extensible tab completion |
332 | * DDS plugin - better format results with Data::Dump::Streamer |
333 | * DDC plugin - even better format results with Data::Dumper::Concise |
334 | * INC completion driver - tab complete module names in use and require |
335 | * Interrupt plugin - traps SIGINT to kill long-running lines |
336 | * Keywords completion driver - tab complete Perl keywords and operators |
337 | * LexEnv plugin - variables declared with "my" persist between statements |
338 | * MultiLine::PPI plugin - continue reading lines until all blocks are closed |
339 | * Nopaste plugin - upload a session\'s input and output to a Pastebin |
340 | * PPI plugin - PPI dumping of Perl code |
341 | * Refresh plugin - automatically reload libraries with Module::Refresh |
408564af |
342 | |
1282c944 |
343 | =head1 SEE ALSO |
344 | |
345 | =for :list |
346 | * L<A comparison of various REPLs|http://shadow.cat/blog/matt-s-trout/mstpan-17/> |
347 | |
59aedffc |
348 | =cut |