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 | |
afe61f9c |
8 | with 'MooseX::Object::Pluggable'; |
9 | |
e22aa835 |
10 | use Devel::REPL::Error; |
11 | |
afe61f9c |
12 | has 'term' => ( |
b595a818 |
13 | is => 'rw', |
2df2b90e |
14 | lazy => 1, |
afe61f9c |
15 | default => sub { Term::ReadLine->new('Perl REPL') } |
16 | ); |
17 | |
18 | has 'prompt' => ( |
b595a818 |
19 | is => 'rw', |
afe61f9c |
20 | default => sub { '$ ' } |
21 | ); |
22 | |
23 | has 'out_fh' => ( |
b595a818 |
24 | is => 'rw', |
25 | lazy => 1, |
afe61f9c |
26 | default => sub { shift->term->OUT || \*STDOUT; } |
27 | ); |
28 | |
57719095 |
29 | has 'exit_repl' => ( |
b595a818 |
30 | is => 'rw', |
57719095 |
31 | default => sub { 0 } |
32 | ); |
33 | |
afe61f9c |
34 | sub run { |
35 | my ($self) = @_; |
e22aa835 |
36 | while ($self->run_once_safely) { |
57719095 |
37 | # keep looping unless we want to exit REPL |
38 | last if $self->exit_repl; |
afe61f9c |
39 | } |
40 | } |
41 | |
e22aa835 |
42 | sub run_once_safely { |
43 | my ($self, @args) = @_; |
44 | |
45 | my $ret = eval { $self->run_once(@args) }; |
46 | |
47 | if ($@) { |
48 | my $error = $@; |
49 | eval { $self->print("Error! - $error\n"); }; |
50 | return 1; |
51 | } else { |
52 | return $ret; |
53 | } |
54 | } |
55 | |
afe61f9c |
56 | sub run_once { |
57 | my ($self) = @_; |
e22aa835 |
58 | |
afe61f9c |
59 | my $line = $self->read; |
57719095 |
60 | return unless defined($line); # undefined value == EOF |
e22aa835 |
61 | |
62 | my @ret = $self->formatted_eval($line); |
63 | |
57719095 |
64 | $self->print(@ret) unless $self->exit_repl; |
e22aa835 |
65 | |
afe61f9c |
66 | return 1; |
67 | } |
68 | |
e22aa835 |
69 | sub formatted_eval { |
70 | my ( $self, @args ) = @_; |
71 | |
72 | my @ret = $self->eval(@args); |
73 | |
74 | return $self->format(@ret); |
75 | } |
76 | |
77 | sub format { |
78 | my ( $self, @stuff ) = @_; |
79 | |
c3bbf326 |
80 | if ( $self->is_error($stuff[0]) ) { |
e22aa835 |
81 | return $self->format_error(@stuff); |
82 | } else { |
83 | return $self->format_result(@stuff); |
84 | } |
85 | } |
86 | |
87 | sub format_result { |
88 | my ( $self, @stuff ) = @_; |
89 | |
90 | return @stuff; |
91 | } |
92 | |
93 | sub format_error { |
94 | my ( $self, $error ) = @_; |
95 | return $error->stringify; |
96 | } |
97 | |
c3bbf326 |
98 | sub is_error { |
99 | my ( $self, $thingy ) = @_; |
100 | blessed($thingy) and $thingy->isa("Devel::REPL::Error"); |
101 | } |
102 | |
afe61f9c |
103 | sub read { |
104 | my ($self) = @_; |
105 | return $self->term->readline($self->prompt); |
106 | } |
107 | |
911a1c24 |
108 | sub eval { |
109 | my ($self, $line) = @_; |
c3bbf326 |
110 | my $compiled = $self->compile($line); |
111 | return $compiled unless defined($compiled) and not $self->is_error($compiled); |
112 | return $self->execute($compiled); |
911a1c24 |
113 | } |
114 | |
115 | sub compile { |
e22aa835 |
116 | my ( $_REPL, @args ) = @_; |
117 | my $compiled = eval $_REPL->wrap_as_sub(@args); |
c3bbf326 |
118 | return $_REPL->error_return("Compile error", $@) if $@; |
911a1c24 |
119 | return $compiled; |
120 | } |
121 | |
122 | sub wrap_as_sub { |
e22aa835 |
123 | my ($self, $line, %args) = @_; |
124 | return qq!sub {\n!. ( $args{no_mangling} ? $line : $self->mangle_line($line) ).qq!\n}\n!; |
911a1c24 |
125 | } |
126 | |
127 | sub mangle_line { |
128 | my ($self, $line) = @_; |
129 | return $line; |
130 | } |
131 | |
afe61f9c |
132 | sub execute { |
48ddfeae |
133 | my ($self, $to_exec, @args) = @_; |
134 | my @ret = eval { $to_exec->(@args) }; |
135 | return $self->error_return("Runtime error", $@) if $@; |
afe61f9c |
136 | return @ret; |
137 | } |
138 | |
911a1c24 |
139 | sub error_return { |
140 | my ($self, $type, $error) = @_; |
e22aa835 |
141 | return Devel::REPL::Error->new( type => $type, message => $error ); |
911a1c24 |
142 | } |
143 | |
afe61f9c |
144 | sub print { |
145 | my ($self, @ret) = @_; |
146 | my $fh = $self->out_fh; |
59aedffc |
147 | no warnings 'uninitialized'; |
afe61f9c |
148 | print $fh "@ret"; |
a66625d6 |
149 | print $fh "\n" if $self->term->ReadLine =~ /Gnu/; |
afe61f9c |
150 | } |
151 | |
59aedffc |
152 | =head1 NAME |
153 | |
154 | Devel::REPL - a modern perl interactive shell |
155 | |
156 | =head1 SYNOPSIS |
157 | |
158 | my $repl = Devel::REPL->new; |
159 | $repl->load_plugin($_) for qw(History LexEnv); |
160 | $repl->run |
161 | |
162 | Alternatively, use the 're.pl' script installed with the distribution |
163 | |
950232b2 |
164 | system$ re.pl |
165 | |
408564af |
166 | =head1 DESCRIPTION |
167 | |
168 | This is an interactive shell for Perl, commonly known as a REPL - Read, |
169 | Evaluate, Print, Loop. The shell provides for rapid development or testing |
170 | of code without the need to create a temporary source code file. |
171 | |
172 | Through a plugin system, many features are available on demand. You can also |
173 | tailor the environment through the use of profiles and run control files, for |
174 | example to pre-load certain Perl modules when working on a particular project. |
175 | |
176 | =head1 USAGE |
177 | |
178 | To start a shell, follow one of the examples in the L</"SYNOPSIS"> above. |
179 | |
180 | Once running, the shell accepts and will attempt to execute any code given. If |
181 | the code executes successfully you'll be shown the result, otherwise an error |
182 | message will be returned. Here are a few examples: |
183 | |
184 | $_ print "Hello, world!\n" |
185 | Hello, world! |
186 | 1 |
187 | $_ nosuchfunction |
188 | Compile error: Bareword "nosuchfunction" not allowed while "strict subs" in use at (eval 130) line 5. |
20d9434d |
189 | |
190 | $_ |
408564af |
191 | |
192 | In the first example above you see the output of the command (C<Hello, |
193 | world!>), if any, and then the return value of the statement (C<1>). Following |
194 | that example, an error is returned when the execution of some code fails. |
195 | |
196 | Note that the lack of semicolon on the end is not a mistake - the code is |
197 | run inside a Block structure (to protect the REPL in case the code blows up), |
198 | which means a single statement doesn't require the semicolon. You can add one |
199 | if you like, though. |
200 | |
6aa58492 |
201 | If you followed the first example in the L</"SYNOPSIS"> above, you'll have the |
408564af |
202 | History and LexEnv plugins loaded (and there are many more available). |
203 | Although the shell might support "up-arrow" history, the History plugin adds |
204 | "bang" history to that so you can re-execute chosen commands (with e.g. |
205 | C<!53>). The LexEnv plugin ensures that lexical variables declared with the |
206 | C<my> keyword will automatically persist between statements executed in the |
207 | REPL shell. |
208 | |
209 | When you C<use> any Perl module, the C<import()> will work as expected - the |
210 | exported functions from that module are available for immediate use: |
211 | |
212 | $_ carp "I'm dieeeing!\n" |
213 | String found where operator expected at (eval 129) line 5, near "carp "I'm dieeeing!\n"" |
214 | (Do you need to predeclare carp?) |
215 | Compile error: syntax error at (eval 129) line 5, near "carp "I'm dieeeing!\n"" |
216 | BEGIN not safe after errors--compilation aborted at (eval 129) line 5. |
20d9434d |
217 | |
218 | $_ use Carp |
219 | |
408564af |
220 | $_ carp "I'm dieeeing!\n" |
221 | I'm dieeeing! |
222 | at /usr/share/perl5/Lexical/Persistence.pm line 327 |
223 | 1 |
20d9434d |
224 | $_ |
408564af |
225 | |
73d11b24 |
226 | To quit from the shell, hit C<Ctrl+D> or C<Ctrl+C>. |
227 | |
228 | MSWin32 NOTE: control keys won't work if TERM=dumb |
229 | because readline functionality will be disabled. |
230 | |
408564af |
231 | |
232 | =head2 Run Control Files |
233 | |
234 | For particular projects you might well end up running the same commands each |
235 | time the REPL shell starts up - loading Perl modules, setting configuration, |
236 | and so on. A run control file lets you have this done automatically, and you |
237 | can have multiple files for different projects. |
238 | |
239 | By default the C<re.pl> program looks for C<< $HOME/.re.pl/repl.rc >>, and |
240 | runs whatever code is in there as if you had entered it at the REPL shell |
241 | yourself. |
242 | |
243 | To set a new run control file that's also in that directory, pass it as a |
244 | filename like so: |
245 | |
246 | system$ re.pl --rcfile myproject.pc |
247 | |
0e0d2539 |
248 | If the filename happens to contain a forward slash, then it's used absolutely, |
408564af |
249 | or realive to the current working directory: |
250 | |
251 | system$ re.pl --rcfile /path/to/my/project/repl.rc |
252 | |
253 | Within the run control file you might want to load plugins. This is covered in |
254 | L</"The REPL shell object"> section, below. |
255 | |
256 | =head2 Profiles |
257 | |
258 | To allow for the sharing of run control files, you can fashion them into a |
259 | Perl module for distribution (perhaps via the CPAN). For more information on |
260 | this feature, please see the L<Devel::REPL::Profile> manual page. |
261 | |
e72070d7 |
262 | A C<Standard> profile ships with C<Devel::REPL>; it loads the following plugins |
263 | (note that some of these require optional features -- or you can also use the |
264 | C<Minimal> profile): |
408564af |
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 | |
0e0d2539 |
324 | Plugins are a way to add functionality to the REPL shell, and take advantage of |
408564af |
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 | |
e72070d7 |
358 | =head1 OPTIONAL FEATURES |
408564af |
359 | |
e72070d7 |
360 | 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 |
361 | additional plugins. You can install any of these features by installing this |
362 | distribution interactively (e.g. C<cpanm --interactive Devel::REPL>). |
408564af |
363 | |
e72070d7 |
364 | =for comment I hope to automatically generate this data via a Pod::Weaver section |
ab213f1f |
365 | |
366 | =over 4 |
367 | |
e72070d7 |
368 | =item * Completion plugin - extensible tab completion |
ab213f1f |
369 | |
e72070d7 |
370 | =item * DDS plugin - better format results with Data::Dump::Streamer |
408564af |
371 | |
e72070d7 |
372 | =item * DDC plugin - even better format results with Data::Dumper::Concise |
408564af |
373 | |
e72070d7 |
374 | =item * INC completion driver - tab complete module names in use and require |
408564af |
375 | |
e72070d7 |
376 | =item * Interrupt plugin - traps SIGINT to kill long-running lines |
408564af |
377 | |
e72070d7 |
378 | =item * Keywords completion driver - tab complete Perl keywords and operators |
73d11b24 |
379 | |
e72070d7 |
380 | =item * LexEnv plugin - variables declared with "my" persist between statements |
73d11b24 |
381 | |
e72070d7 |
382 | =item * MultiLine::PPI plugin - continue reading lines until all blocks are closed |
408564af |
383 | |
e72070d7 |
384 | =item * Nopaste plugin - upload a session\'s input and output to a Pastebin |
408564af |
385 | |
e72070d7 |
386 | =item * PPI plugin - PPI dumping of Perl code |
ab213f1f |
387 | |
e72070d7 |
388 | =item * Refresh plugin - automatically reload libraries with Module::Refresh |
ab213f1f |
389 | |
408564af |
390 | =back |
391 | |
59aedffc |
392 | =head1 AUTHOR |
393 | |
394 | Matt S Trout - mst (at) shadowcatsystems.co.uk (L<http://www.shadowcatsystems.co.uk/>) |
395 | |
c1d5d500 |
396 | =head1 CONTRIBUTORS |
397 | |
398 | =over 4 |
399 | |
400 | =item Stevan Little - stevan (at) iinteractive.com |
401 | |
402 | =item Alexis Sukrieh - sukria+perl (at) sukria.net |
403 | |
404 | =item epitaph |
405 | |
406 | =item mgrimes - mgrimes (at) cpan dot org |
407 | |
408 | =item Shawn M Moore - sartak (at) gmail.com |
409 | |
ab213f1f |
410 | =item Oliver Gorwits - oliver on irc.perl.org |
6aa58492 |
411 | |
da4881b1 |
412 | =item Andrew Moore - C<< <amoore@cpan.org> >> |
413 | |
88d6bf36 |
414 | =item Norbert Buchmuller C<< <norbi@nix.hu> >> |
415 | |
d13037d5 |
416 | =item Dave Houston C<< <dhouston@cpan.org> >> |
417 | |
73d11b24 |
418 | =item Chris Marshall |
419 | |
b28cbcb7 |
420 | =item Karen Etheridge C<< <ether@cpan.org> >> |
421 | |
c1d5d500 |
422 | =back |
423 | |
59aedffc |
424 | =head1 LICENSE |
425 | |
426 | This library is free software under the same terms as perl itself |
427 | |
428 | =cut |
429 | |
afe61f9c |
430 | 1; |