Commit | Line | Data |
afe61f9c |
1 | package Devel::REPL; |
2 | |
3 | use Term::ReadLine; |
e2d0b019 |
4 | use Moo; |
5 | use namespace::sweep; |
089a0c4e |
6 | use 5.008001; # backwards compat, doesn't warn like 5.8.1 |
59aedffc |
7 | |
bb1f569c |
8 | our $VERSION = '1.003014'; |
afe61f9c |
9 | |
e22aa835 |
10 | use Devel::REPL::Error; |
e2d0b019 |
11 | use Scalar::Util qw/blessed/; |
12 | use Module::Runtime (); |
13 | |
14 | sub load_plugin { |
15 | my ($self, $plugin) = @_; |
16 | $plugin = "Devel::REPL::Plugin::$plugin"; |
17 | Module::Runtime::use_module("$plugin"); |
18 | if (my $pre = $plugin->can('BEFORE_PLUGIN')) { |
19 | $pre->($self, $plugin); |
20 | } |
21 | Moo::Role->apply_roles_to_package( |
22 | 'Devel::REPL', $plugin |
23 | ); |
24 | if (my $pre = $plugin->can('AFTER_PLUGIN')) { |
25 | $pre->($self, $plugin); |
26 | } |
27 | } |
e22aa835 |
28 | |
afe61f9c |
29 | has 'term' => ( |
30 | is => 'rw', required => 1, |
31 | default => sub { Term::ReadLine->new('Perl REPL') } |
32 | ); |
33 | |
34 | has 'prompt' => ( |
35 | is => 'rw', required => 1, |
36 | default => sub { '$ ' } |
37 | ); |
38 | |
39 | has 'out_fh' => ( |
40 | is => 'rw', required => 1, lazy => 1, |
41 | default => sub { shift->term->OUT || \*STDOUT; } |
42 | ); |
43 | |
57719095 |
44 | has 'exit_repl' => ( |
45 | is => 'rw', required => 1, |
46 | default => sub { 0 } |
47 | ); |
48 | |
afe61f9c |
49 | sub run { |
50 | my ($self) = @_; |
e22aa835 |
51 | while ($self->run_once_safely) { |
57719095 |
52 | # keep looping unless we want to exit REPL |
53 | last if $self->exit_repl; |
afe61f9c |
54 | } |
55 | } |
56 | |
e22aa835 |
57 | sub run_once_safely { |
58 | my ($self, @args) = @_; |
59 | |
60 | my $ret = eval { $self->run_once(@args) }; |
61 | |
62 | if ($@) { |
63 | my $error = $@; |
64 | eval { $self->print("Error! - $error\n"); }; |
65 | return 1; |
66 | } else { |
67 | return $ret; |
68 | } |
69 | } |
70 | |
afe61f9c |
71 | sub run_once { |
72 | my ($self) = @_; |
e22aa835 |
73 | |
afe61f9c |
74 | my $line = $self->read; |
57719095 |
75 | return unless defined($line); # undefined value == EOF |
e22aa835 |
76 | |
77 | my @ret = $self->formatted_eval($line); |
78 | |
57719095 |
79 | $self->print(@ret) unless $self->exit_repl; |
e22aa835 |
80 | |
afe61f9c |
81 | return 1; |
82 | } |
83 | |
e22aa835 |
84 | sub formatted_eval { |
85 | my ( $self, @args ) = @_; |
86 | |
87 | my @ret = $self->eval(@args); |
88 | |
89 | return $self->format(@ret); |
90 | } |
91 | |
92 | sub format { |
93 | my ( $self, @stuff ) = @_; |
94 | |
c3bbf326 |
95 | if ( $self->is_error($stuff[0]) ) { |
e22aa835 |
96 | return $self->format_error(@stuff); |
97 | } else { |
98 | return $self->format_result(@stuff); |
99 | } |
100 | } |
101 | |
102 | sub format_result { |
103 | my ( $self, @stuff ) = @_; |
104 | |
105 | return @stuff; |
106 | } |
107 | |
108 | sub format_error { |
109 | my ( $self, $error ) = @_; |
110 | return $error->stringify; |
111 | } |
112 | |
c3bbf326 |
113 | sub is_error { |
114 | my ( $self, $thingy ) = @_; |
115 | blessed($thingy) and $thingy->isa("Devel::REPL::Error"); |
116 | } |
117 | |
afe61f9c |
118 | sub read { |
119 | my ($self) = @_; |
120 | return $self->term->readline($self->prompt); |
121 | } |
122 | |
911a1c24 |
123 | sub eval { |
124 | my ($self, $line) = @_; |
c3bbf326 |
125 | my $compiled = $self->compile($line); |
126 | return $compiled unless defined($compiled) and not $self->is_error($compiled); |
127 | return $self->execute($compiled); |
911a1c24 |
128 | } |
129 | |
130 | sub compile { |
e22aa835 |
131 | my ( $_REPL, @args ) = @_; |
132 | my $compiled = eval $_REPL->wrap_as_sub(@args); |
c3bbf326 |
133 | return $_REPL->error_return("Compile error", $@) if $@; |
911a1c24 |
134 | return $compiled; |
135 | } |
136 | |
137 | sub wrap_as_sub { |
e22aa835 |
138 | my ($self, $line, %args) = @_; |
139 | return qq!sub {\n!. ( $args{no_mangling} ? $line : $self->mangle_line($line) ).qq!\n}\n!; |
911a1c24 |
140 | } |
141 | |
142 | sub mangle_line { |
143 | my ($self, $line) = @_; |
144 | return $line; |
145 | } |
146 | |
afe61f9c |
147 | sub execute { |
48ddfeae |
148 | my ($self, $to_exec, @args) = @_; |
149 | my @ret = eval { $to_exec->(@args) }; |
150 | return $self->error_return("Runtime error", $@) if $@; |
afe61f9c |
151 | return @ret; |
152 | } |
153 | |
911a1c24 |
154 | sub error_return { |
155 | my ($self, $type, $error) = @_; |
e22aa835 |
156 | return Devel::REPL::Error->new( type => $type, message => $error ); |
911a1c24 |
157 | } |
158 | |
afe61f9c |
159 | sub print { |
160 | my ($self, @ret) = @_; |
161 | my $fh = $self->out_fh; |
59aedffc |
162 | no warnings 'uninitialized'; |
afe61f9c |
163 | print $fh "@ret"; |
a66625d6 |
164 | print $fh "\n" if $self->term->ReadLine =~ /Gnu/; |
afe61f9c |
165 | } |
166 | |
59aedffc |
167 | =head1 NAME |
168 | |
169 | Devel::REPL - a modern perl interactive shell |
170 | |
171 | =head1 SYNOPSIS |
172 | |
173 | my $repl = Devel::REPL->new; |
174 | $repl->load_plugin($_) for qw(History LexEnv); |
175 | $repl->run |
176 | |
177 | Alternatively, use the 're.pl' script installed with the distribution |
178 | |
950232b2 |
179 | system$ re.pl |
180 | |
408564af |
181 | =head1 DESCRIPTION |
182 | |
183 | This is an interactive shell for Perl, commonly known as a REPL - Read, |
184 | Evaluate, Print, Loop. The shell provides for rapid development or testing |
185 | of code without the need to create a temporary source code file. |
186 | |
187 | Through a plugin system, many features are available on demand. You can also |
188 | tailor the environment through the use of profiles and run control files, for |
189 | example to pre-load certain Perl modules when working on a particular project. |
190 | |
191 | =head1 USAGE |
192 | |
193 | To start a shell, follow one of the examples in the L</"SYNOPSIS"> above. |
194 | |
195 | Once running, the shell accepts and will attempt to execute any code given. If |
196 | the code executes successfully you'll be shown the result, otherwise an error |
197 | message will be returned. Here are a few examples: |
198 | |
199 | $_ print "Hello, world!\n" |
200 | Hello, world! |
201 | 1 |
202 | $_ nosuchfunction |
203 | Compile error: Bareword "nosuchfunction" not allowed while "strict subs" in use at (eval 130) line 5. |
20d9434d |
204 | |
205 | $_ |
408564af |
206 | |
207 | In the first example above you see the output of the command (C<Hello, |
208 | world!>), if any, and then the return value of the statement (C<1>). Following |
209 | that example, an error is returned when the execution of some code fails. |
210 | |
211 | Note that the lack of semicolon on the end is not a mistake - the code is |
212 | run inside a Block structure (to protect the REPL in case the code blows up), |
213 | which means a single statement doesn't require the semicolon. You can add one |
214 | if you like, though. |
215 | |
6aa58492 |
216 | If you followed the first example in the L</"SYNOPSIS"> above, you'll have the |
408564af |
217 | History and LexEnv plugins loaded (and there are many more available). |
218 | Although the shell might support "up-arrow" history, the History plugin adds |
219 | "bang" history to that so you can re-execute chosen commands (with e.g. |
220 | C<!53>). The LexEnv plugin ensures that lexical variables declared with the |
221 | C<my> keyword will automatically persist between statements executed in the |
222 | REPL shell. |
223 | |
224 | When you C<use> any Perl module, the C<import()> will work as expected - the |
225 | exported functions from that module are available for immediate use: |
226 | |
227 | $_ carp "I'm dieeeing!\n" |
228 | String found where operator expected at (eval 129) line 5, near "carp "I'm dieeeing!\n"" |
229 | (Do you need to predeclare carp?) |
230 | Compile error: syntax error at (eval 129) line 5, near "carp "I'm dieeeing!\n"" |
231 | BEGIN not safe after errors--compilation aborted at (eval 129) line 5. |
20d9434d |
232 | |
233 | $_ use Carp |
234 | |
408564af |
235 | $_ carp "I'm dieeeing!\n" |
236 | I'm dieeeing! |
237 | at /usr/share/perl5/Lexical/Persistence.pm line 327 |
238 | 1 |
20d9434d |
239 | $_ |
408564af |
240 | |
73d11b24 |
241 | To quit from the shell, hit C<Ctrl+D> or C<Ctrl+C>. |
242 | |
243 | MSWin32 NOTE: control keys won't work if TERM=dumb |
244 | because readline functionality will be disabled. |
245 | |
408564af |
246 | |
247 | =head2 Run Control Files |
248 | |
249 | For particular projects you might well end up running the same commands each |
250 | time the REPL shell starts up - loading Perl modules, setting configuration, |
251 | and so on. A run control file lets you have this done automatically, and you |
252 | can have multiple files for different projects. |
253 | |
254 | By default the C<re.pl> program looks for C<< $HOME/.re.pl/repl.rc >>, and |
255 | runs whatever code is in there as if you had entered it at the REPL shell |
256 | yourself. |
257 | |
258 | To set a new run control file that's also in that directory, pass it as a |
259 | filename like so: |
260 | |
261 | system$ re.pl --rcfile myproject.pc |
262 | |
263 | If the filename happens to contain a forwardslash, then it's used absolutely, |
264 | or realive to the current working directory: |
265 | |
266 | system$ re.pl --rcfile /path/to/my/project/repl.rc |
267 | |
268 | Within the run control file you might want to load plugins. This is covered in |
269 | L</"The REPL shell object"> section, below. |
270 | |
271 | =head2 Profiles |
272 | |
273 | To allow for the sharing of run control files, you can fashion them into a |
274 | Perl module for distribution (perhaps via the CPAN). For more information on |
275 | this feature, please see the L<Devel::REPL::Profile> manual page. |
276 | |
277 | A default profile ships with C<Devel::REPL>; it loads the following plugins: |
278 | |
279 | =over 4 |
280 | |
281 | =item * |
282 | |
283 | L<Devel::REPL::Plugin::History> |
284 | |
285 | =item * |
286 | |
287 | L<Devel::REPL::Plugin::LexEnv> |
288 | |
289 | =item * |
290 | |
291 | L<Devel::REPL::Plugin::DDS> |
292 | |
293 | =item * |
294 | |
295 | L<Devel::REPL::Plugin::Packages> |
296 | |
297 | =item * |
298 | |
299 | L<Devel::REPL::Plugin::Commands> |
300 | |
301 | =item * |
302 | |
303 | L<Devel::REPL::Plugin::MultiLine::PPI> |
304 | |
071c41fa |
305 | =item * |
306 | |
307 | L<Devel::REPL::Plugin::Colors> |
308 | |
309 | =item * |
310 | |
311 | L<Devel::REPL::Plugin::Completion> |
312 | |
313 | =item * |
314 | |
315 | L<Devel::REPL::Plugin::CompletionDriver::INC> |
316 | |
317 | =item * |
318 | |
319 | L<Devel::REPL::Plugin::CompletionDriver::LexEnv> |
320 | |
321 | =item * |
322 | |
323 | L<Devel::REPL::Plugin::CompletionDriver::Keywords> |
324 | |
325 | =item * |
326 | |
327 | L<Devel::REPL::Plugin::CompletionDriver::Methods> |
328 | |
329 | =item * |
330 | |
331 | L<Devel::REPL::Plugin::ReadlineHistory> |
332 | |
408564af |
333 | =back |
334 | |
335 | =head2 Plugins |
336 | |
337 | Plugins are a way to add funcionality to the REPL shell, and take advantage of |
338 | C<Devel::REPL> being based on the L<Moose> object system for Perl 5. This |
339 | means it's simple to 'hook into' many steps of the R-E-P-L process. Plugins |
340 | can change the way commands are interpreted, or the way their results are |
341 | output, or even add commands to the shell environment. |
342 | |
343 | A number of plugins ship with C<Devel::REPL>, and more are available on the |
344 | CPAN. Some of the shipped plugins are loaded in the default profile, mentioned |
cfb85b27 |
345 | above. These plugins can be loaded in your C<< $HOME/.re.pl/repl.rc >> like: |
346 | |
347 | load_plugin qw( CompletionDriver::Global DumpHistory ); |
408564af |
348 | |
349 | Writing your own plugins is not difficult, and is discussed in the |
350 | L<Devel::REPL::Plugin> manual page, along with links to the manual pages of |
351 | all the plugins shipped with C<Devel::REPL>. |
352 | |
353 | =head2 The REPL shell object |
354 | |
355 | From time to time you'll want to interact with or manipulate the |
356 | C<Devel::REPL> shell object itself; that is, the instance of the shell you're |
357 | currently running. |
358 | |
359 | The object is always available through the C<$_REPL> variable. One common |
360 | requirement is to load an additional plugin, after your profile and run |
361 | control files have already been executed: |
362 | |
363 | $_ $_REPL->load_plugin('Timing'); |
364 | 1 |
365 | $_ print "Hello again, world!\n" |
366 | Hello again, world! |
367 | Took 0.00148296356201172 seconds. |
368 | 1 |
369 | $_ |
370 | |
371 | =head1 REQUIREMENTS |
372 | |
373 | In addition to the contents of the standard Perl distribution, you will need |
374 | the following: |
375 | |
376 | =over 4 |
377 | |
378 | =item * |
379 | |
73d11b24 |
380 | L<Moose> >= 0.74 |
6aa58492 |
381 | |
382 | =item * |
383 | |
73d11b24 |
384 | L<MooseX::Getopt> >= 0.18 |
408564af |
385 | |
386 | =item * |
387 | |
e2d0b019 |
388 | L<namespace::sweep> |
408564af |
389 | |
390 | =item * |
391 | |
392 | L<File::HomeDir> |
393 | |
394 | =item * |
395 | |
ab213f1f |
396 | L<Task::Weaken> |
397 | |
73d11b24 |
398 | =item * |
399 | |
400 | L<B::Concise> |
401 | |
402 | =item * |
403 | |
404 | L<Term::ANSIColor> |
405 | |
406 | =item * |
407 | |
408 | L<Devel::Peek> |
409 | |
ab213f1f |
410 | =back |
411 | |
412 | Optionally, some plugins if installed will require the following modules: |
413 | |
414 | =over 4 |
415 | |
416 | =item * |
417 | |
418 | L<PPI> |
408564af |
419 | |
420 | =item * |
421 | |
6aa58492 |
422 | L<Data::Dump::Streamer> |
408564af |
423 | |
424 | =item * |
425 | |
73d11b24 |
426 | L<Data::Dumper::Concise> |
427 | |
428 | =item * |
429 | |
ab213f1f |
430 | L<File::Next> |
408564af |
431 | |
432 | =item * |
433 | |
73d11b24 |
434 | L<Sys::SigAction> |
435 | |
436 | =item * |
437 | |
408564af |
438 | L<B::Keywords> |
439 | |
440 | =item * |
441 | |
ab213f1f |
442 | L<Lexical::Persistence> |
408564af |
443 | |
444 | =item * |
445 | |
446 | L<App::Nopaste> |
447 | |
ab213f1f |
448 | =item * |
449 | |
450 | L<Module::Refresh> |
451 | |
408564af |
452 | =back |
453 | |
59aedffc |
454 | =head1 AUTHOR |
455 | |
456 | Matt S Trout - mst (at) shadowcatsystems.co.uk (L<http://www.shadowcatsystems.co.uk/>) |
457 | |
c1d5d500 |
458 | =head1 CONTRIBUTORS |
459 | |
460 | =over 4 |
461 | |
462 | =item Stevan Little - stevan (at) iinteractive.com |
463 | |
464 | =item Alexis Sukrieh - sukria+perl (at) sukria.net |
465 | |
466 | =item epitaph |
467 | |
468 | =item mgrimes - mgrimes (at) cpan dot org |
469 | |
470 | =item Shawn M Moore - sartak (at) gmail.com |
471 | |
ab213f1f |
472 | =item Oliver Gorwits - oliver on irc.perl.org |
6aa58492 |
473 | |
da4881b1 |
474 | =item Andrew Moore - C<< <amoore@cpan.org> >> |
475 | |
88d6bf36 |
476 | =item Norbert Buchmuller C<< <norbi@nix.hu> >> |
477 | |
d13037d5 |
478 | =item Dave Houston C<< <dhouston@cpan.org> >> |
479 | |
73d11b24 |
480 | =item Chris Marshall |
481 | |
c1d5d500 |
482 | =back |
483 | |
59aedffc |
484 | =head1 LICENSE |
485 | |
486 | This library is free software under the same terms as perl itself |
487 | |
488 | =cut |
489 | |
afe61f9c |
490 | 1; |