1 package Function::Parameters;
9 use B::Hooks::EndOfScope;
16 my $defcaller = (caller $start)[0];
17 my $caller = $defcaller;
19 for (my $level = $start; ; ++$level) {
20 my ($pkg, $function) = (caller $level)[0, 3] or last;
21 #warn "? $pkg, $function";
22 $function =~ /::import\z/ or return $caller;
28 sub _fun ($) { $_[0] }
32 my $caller = guess_caller;
33 #warn "caller = $caller";
35 Devel::Declare->setup_for(
37 { fun => { const => \&parser } }
41 *{$caller . '::fun'} = \&_fun;
45 my ($offset, $name) = @_;
47 my $line = Devel::Declare::get_linestr();
48 substr $line, $offset + 1, 0, "\x{20de}\e[m";
49 substr $line, $offset, 0, "\e[31;1m";
50 print STDERR "$name($offset)>> $line\n";
54 my ($declarator, $start) = @_;
56 my $line = Devel::Declare::get_linestr();
59 my $_file = PL_compiling->file;
60 my $_line = PL_compiling->line;
62 my $n = $_line + substr($line, $start, $offset - $start) =~ tr[\n][];
63 die join('', @_) . " at $_file line $n\n";
67 my $atomically = sub {
71 my @ret = eval { $pars->(@_) };
76 wantarray ? @ret : $ret[0]
82 my @ret = eval { $pars->() };
86 wantarray ? @ret : $ret[0]
91 my $skip = Devel::Declare::toke_skipspace($offset);
93 $skip == -$offset or die "Internal error: offset=$offset, skip=$skip";
94 Devel::Declare::set_linestr($line);
97 $line = Devel::Declare::get_linestr();
98 #warn "toke_skipspace($offset) = $skip\n== $line";
102 $offset += Devel::Declare::toke_move_past_token($offset);
104 my $manip_start = $offset;
107 if (my $len = Devel::Declare::toke_scan_word($offset, 1)) {
108 $name = substr $line, $offset, $len;
113 my $scan_token = sub {
115 my $len = length $str;
116 substr($line, $offset, $len) eq $str or $fail->(qq{Missing "$str"});
122 my $len = Devel::Declare::toke_scan_word($offset, 0) or $fail->('Missing identifier');
123 my $name = substr $line, $offset, $len;
129 my $scan_var = $atomically->(sub {
130 (my $sigil = substr($line, $offset, 1)) =~ /^[\$\@%]\z/ or $fail->('Missing [$@%]');
133 my $name = $scan_id->();
137 my $separated_by = $atomically->(sub {
138 my ($sep, $pars) = @_;
139 my $len = length $sep;
140 defined(my $x = $try->($pars)) or return;
143 substr($line, $offset, $len) eq $sep or return @res;
146 push @res, $pars->();
150 my $many_till = $atomically->(sub {
151 my ($end, $pars) = @_;
152 my $len = length $end;
154 until (substr($line, $offset, $len) eq $end) {
155 push @res, $pars->();
160 my $scan_params = $atomically->(sub {
161 if ($try->(sub { $scan_token->('('); 1 })) {
162 my @param = $separated_by->(',', $scan_var);
169 my @param = $scan_params->();
171 my $scan_pargroup_opt = sub {
172 substr($line, $offset, 1) eq '(' or return '';
173 my $len = Devel::Declare::toke_scan_str($offset);
174 my $res = Devel::Declare::get_lex_stuff();
175 Devel::Declare::clear_lex_stuff();
176 $res eq '' and $fail->(qq{Can't find ")" anywhere before EOF});
182 my $scan_attr = sub {
183 my $name = $scan_id->();
184 my $param = $scan_pargroup_opt->() || '';
188 my $scan_attributes = $atomically->(sub {
189 $try->(sub { $scan_token->(':'); 1 }) or return '', [];
190 my $proto = $scan_pargroup_opt->();
191 my @attrs = $many_till->('{', $scan_attr);
192 ' ' . $proto, \@attrs
195 my ($proto, $attributes) = $scan_attributes->();
196 my $attr = @$attributes ? ' : ' . join(' ', @$attributes) : '';
200 my $manip_end = $offset;
201 my $manip_len = $manip_end - $manip_start;
202 #print STDERR "($manip_start:$manip_len:$manip_end)\n";
204 my $params = @param ? 'my (' . join(', ', @param) . ') = @_;' : '';
209 my $pkg = __PACKAGE__;
210 #print STDERR "($manip_start:$manip_len) [$line]\n";
211 substr $line, $manip_start, $manip_len, " do { sub $name$proto; sub $name$proto$attr { BEGIN { ${pkg}::terminate_me(q[$name]); } $params ";
213 substr $line, $manip_start, $manip_len, " sub$proto$attr { $params ";
215 #print STDERR ".> $line\n";
216 Devel::Declare::set_linestr($line);
222 my $line = Devel::Declare::get_linestr();
223 #print STDERR "~~> $line\n";
224 my $offset = Devel::Declare::get_linestr_offset();
225 substr $line, $offset, 0, " \\&$name };";
226 Devel::Declare::set_linestr($line);
227 #print STDERR "??> $line\n";
237 Function::Parameters - subroutine definitions with parameter lists
241 use Function::Parameters;
243 fun foo($bar, $baz) {
247 fun mymap($fun, @args) :(&@) {
250 push @res, $fun->($_);
255 print "$_\n" for mymap { $_ * 2 } 1 .. 4;
259 This module lets you use parameter lists in your subroutines. Thanks to
260 L<Devel::Declare> it works without source filters.
262 WARNING: This is my first attempt at using L<Devel::Declare> and I have
263 almost no experience with perl's internals. So while this module might
264 appear to work, it could also conceivably make your programs segfault.
265 Consider this module alpha quality.
269 To use this new functionality, you have to use C<fun> instead of C<sub> -
270 C<sub> continues to work as before. The syntax is almost the same as for
271 C<sub>, but after the subroutine name (or directly after C<fun> if you're
272 writing an anonymous sub) you can write a parameter list in parens. This
273 list consists of comma-separated variables.
275 The effect of C<fun foo($bar, $baz) {> is as if you'd written
276 C<sub foo { my ($bar, $baz) = @_; >, i.e. the parameter list is simply
277 copied into C<my> and initialized from L<@_|perlvar/"@_">.
279 =head2 Advanced stuff
281 If you need L<subroutine attributes|perlsub/"Subroutine Attributes">, you can
282 put them after the parameter list with their usual syntax. There's one
283 exception, though: you can only use one colon (to start the attribute list);
284 multiple attributes have to be separated by spaces.
286 Syntactically, these new parameter lists live in the spot normally occupied
287 by L<prototypes|perlsub/"Prototypes">. However, you can include a prototype by
288 specifying it as the first attribute (this is syntactically unambiguous
289 because normal attributes have to start with a letter).
291 Normally, Perl subroutines are not in scope in their own body, meaning the
292 parser doesn't know the name C<foo> or its prototype when processing
293 C<sub foo ($) { foo $bar[1], $bar[0]; }>, parsing it as
294 C<$bar-E<gt>foo([1], $bar[0])>. Yes. You can add parens to change the
295 interpretation of this code, but C<foo($bar[1], $bar[0])> will only trigger
296 a I<foo() called too early to check prototype> warning. This module attempts
297 to fix all of this by adding a subroutine declaration before the definition,
298 so the parser knows the name (and possibly prototype) while it processes the
299 body. Thus C<fun foo($x) :($) { $x }> really turns into
300 C<sub foo ($); sub foo ($) { my ($x) = @_; $x }>.
304 Lukas Mai, C<< <l.mai at web.de> >>
306 =head1 COPYRIGHT & LICENSE
308 Copyright 2009 Lukas Mai.
310 This program is free software; you can redistribute it and/or modify it
311 under the terms of either: the GNU General Public License as published
312 by the Free Software Foundation; or the Artistic License.
314 See http://dev.perl.org/licenses/ for more information.