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