1 package Function::Parameters;
8 use Carp qw(croak confess);
10 use B::Hooks::EndOfScope;
12 our @CARP_NOT = qw(Devel::Declare);
15 # Make our import chainable so a wrapper module that wants to turn on F:P
16 # for its users can just say
17 # sub import { Function::Parameters->import; }
19 # To make that possible we skip all subs named 'import' in our search for the
26 my $defcaller = (caller $start)[0];
27 my $caller = $defcaller;
29 for (my $level = $start; ; ++$level) {
30 my ($pkg, $function) = (caller $level)[0, 3] or last;
31 $function =~ /::import\z/ or return $caller;
38 # Parse import spec and make shit happen.
40 my @bare_arms = qw(function method);
45 @_ or @_ = ('fun', 'method');
46 if (@_ == 1 && ref($_[0]) eq 'HASH') {
47 @_ = map [$_, $_[0]{$_}], keys %{$_[0]}
57 : [$proto, $bare_arms[$bare++] || confess(qq{Don't know what to do with "$proto"})]
59 my ($name, $type) = @$item;
60 $name =~ /^[^\W\d]\w*\z/
61 or confess qq{"$name" doesn't look like a valid identifier};
62 my ($index) = grep $bare_arms[$_] eq $type, 0 .. $#bare_arms
63 or confess qq{"$type" doesn't look like a valid type (one of ${\join ', ', @bare_arms})};
65 $spec{$name} = {const => mk_parse($index)};
68 Devel::Declare->setup_for($victim, \%spec);
69 for my $name (keys %spec) {
71 *{$victim . '::' . $name} = \&_declarator;
77 my $caller = guess_caller;
78 import_into $caller, @_;
86 # Wrapper around substr where param 3 is an end offset, not a length.
90 ? substr $_[0], $_[1], $_[2] - $_[1], $_[3]
91 : substr $_[0], $_[1], $_[2] - $_[1]
96 my $cur = my $start = $ctx->{offset};
97 while (my $d = Devel::Declare::toke_skipspace $cur) {
100 $ctx->{space}{$key} .= _substring Devel::Declare::get_linestr, $start, $cur if $key;
101 $ctx->{offset} = $cur;
106 my $p = $ctx->{offset};
107 my $namlen = Devel::Declare::toke_scan_word $p, !!'handle_package'
109 my $str = Devel::Declare::get_linestr;
110 $ctx->{name} = substr $str, $p, $namlen;
111 $ctx->{offset} += $namlen;
112 _skip_space $ctx, 'name';
117 substr(Devel::Declare::get_linestr, $ctx->{offset}, 1) eq '('
120 _skip_space $ctx, 'params';
125 my $c = substr Devel::Declare::get_linestr, $ctx->{offset}, 1;
127 if ($c =~ /^[\$\@%]\z/) {
129 _skip_space $ctx, "params_$pcount";
130 my $namlen = Devel::Declare::toke_scan_word $ctx->{offset}, !'handle_package'
131 or croak "Missing identifier";
132 my $name = substr Devel::Declare::get_linestr, $ctx->{offset}, $namlen;
133 $ctx->{params} .= $c . $name . ',';
134 $ctx->{offset} += $namlen;
135 _skip_space $ctx, "params_$pcount";
137 $c = substr Devel::Declare::get_linestr, $ctx->{offset}, 1;
140 _skip_space $ctx, "params_$pcount";
148 _skip_space $ctx, 'params';
153 croak "Unexpected EOF in parameter list";
156 croak "Unexpected '$c' in parameter list";
163 my $strlen = Devel::Declare::toke_scan_str $ctx->{offset};
164 $strlen == 0 || $strlen == -1 and return;
166 $strlen < 0 and confess "Devel::Declare::toke_scan_str done fucked up ($strlen); see https://rt.cpan.org/Ticket/Display.html?id=51679";
168 my $str = Devel::Declare::get_lex_stuff;
169 Devel::Declare::clear_lex_stuff;
171 $ctx->{offset} += $strlen;
179 my $savepos = $ctx->{offset};
181 substr(Devel::Declare::get_linestr, $ctx->{offset}, 1) eq ':'
184 _skip_space $ctx, 'proto_tmp';
186 unless (substr(Devel::Declare::get_linestr, $ctx->{offset}, 1) eq '(') {
187 $ctx->{offset} = $savepos;
188 delete $ctx->{space}{proto_tmp};
191 $_->{proto} .= delete $_->{proto_tmp} for $ctx->{space};
193 defined(my $str = _parse_parens $ctx)
194 or croak "Malformed prototype";
195 $ctx->{proto} = $str;
197 _skip_space $ctx, 'proto';
205 if (substr(Devel::Declare::get_linestr, $ctx->{offset}, 1) eq ':') {
207 _skip_space $ctx, "attr_$pcount";
208 } elsif (!defined $ctx->{proto}) {
213 my $namlen = Devel::Declare::toke_scan_word $ctx->{offset}, !'handle_package'
215 $ctx->{attr} .= substr Devel::Declare::get_linestr, $ctx->{offset}, $namlen;
216 $ctx->{offset} += $namlen;
217 _skip_space $ctx, "attr_$pcount";
218 if (substr(Devel::Declare::get_linestr, $ctx->{offset}, 1) eq '(') {
219 defined(my $str = _parse_parens $ctx)
220 or croak "Malformed attribute argument list";
221 $ctx->{attr} .= "($str)";
222 _skip_space $ctx, "attr_$pcount";
226 if (substr(Devel::Declare::get_linestr, $ctx->{offset}, 1) eq ':') {
228 _skip_space $ctx, "attr_$pcount";
234 # fun name (params) :(proto) :attr { ... }
236 # fun (do { sub (proto) :attr { self? my (params) = @_; ... } })
237 # fun (do { sub name (proto); sub name (proto) :attr { self? my (params) = @_; ... } });
240 my ($ctx, $declarator, $implicit_self) = @_;
244 my $skipped = join '', values %{$ctx->{space}};
245 my $lines = $skipped =~ tr/\n//;
246 $gen .= "\n" x $lines;
248 my $proto = defined $ctx->{proto} ? "($ctx->{proto})" : '';
251 if (defined(my $name = $ctx->{name})) {
253 $gen .= " $name$proto;";
259 if (defined $ctx->{attr}) {
260 $gen .= ":$ctx->{attr}";
264 $gen .= "BEGIN{${\__PACKAGE__}::_fini($is_stmt)}";
266 if ($implicit_self) {
267 $gen .= 'my$self=shift;';
269 if (defined $ctx->{params}) {
270 $gen .= "my($ctx->{params})=\@_;";
276 my ($implicit_self) = @_;
279 my ($declarator, $offset_orig) = @_;
281 offset => $offset_orig,
285 $ctx->{offset} += Devel::Declare::toke_move_past_token($ctx->{offset});
288 my $start = $ctx->{offset};
295 my $offset = $ctx->{offset};
297 my $linestr = Devel::Declare::get_linestr;
298 substr($linestr, $offset, 1) eq '{'
299 or croak qq[I was expecting a function body, not "${\substr $linestr, $offset}"];
301 my $gen = _generate $ctx, $declarator, $implicit_self;
302 my $oldlen = $offset + 1 - $start;
303 _substring $linestr, $start, $offset + 1, (' ' x $oldlen) . $gen;
304 Devel::Declare::set_linestr $linestr;
308 # Patch in the end of our synthetic 'do' block, close argument list, and
309 # optionally terminate the statement.
314 my $off = Devel::Declare::get_linestr_offset;
315 my $str = Devel::Declare::get_linestr;
316 substr $str, $off, 0, '})' . ($stmt ? ';' : '');
317 Devel::Declare::set_linestr $str;
327 Function::Parameters - subroutine definitions with parameter lists
331 use Function::Parameters;
333 fun foo($bar, $baz) {
337 fun mymap($fun, @args) :(&@) {
340 push @res, $fun->($_);
345 print "$_\n" for mymap { $_ * 2 } 1 .. 4;
347 method set_name($name) {
348 $self->{name} = $name;
355 use Function::Parameters 'proc', 'meth';
357 my $f = proc ($x) { $x * 2 };
364 This module lets you use parameter lists in your subroutines. Thanks to
365 L<Devel::Declare> it works without source filters.
367 WARNING: This is my first attempt at using L<Devel::Declare> and I have
368 almost no experience with perl's internals. So while this module might
369 appear to work, it could also conceivably make your programs segfault.
370 Consider this module alpha quality.
374 To use this new functionality, you have to use C<fun> instead of C<sub> -
375 C<sub> continues to work as before. The syntax is almost the same as for
376 C<sub>, but after the subroutine name (or directly after C<fun> if you're
377 writing an anonymous sub) you can write a parameter list in parentheses. This
378 list consists of comma-separated variables.
380 The effect of C<fun foo($bar, $baz) {> is as if you'd written
381 C<sub foo { my ($bar, $baz) = @_; >, i.e. the parameter list is simply
382 copied into C<my> and initialized from L<@_|perlvar/"@_">.
384 In addition you can use C<method>, which understands the same syntax as C<fun>
385 but automatically creates a C<$self> variable for you. So by writing
386 C<method foo($bar, $baz) {> you get the same effect as
387 C<sub foo { my $self = shift; my ($bar, $baz) = @_; >.
389 =head2 Customizing the generated keywords
391 You can customize the names of the keywords injected in your package. To do that
392 you pass a hash reference in the import list:
394 use Function::Parameters { proc => 'function', meth => 'method' }; # -or-
395 use Function::Parameters { proc => 'function' }; # -or-
396 use Function::Parameters { meth => 'method' };
398 The first line creates two keywords, C<proc> and C<meth> (for defining
399 functions and methods, respectively). The last two lines only create one
400 keyword. Generally the hash keys can be any identifiers you want while the
401 values have to be either C<function> or C<method>. The difference between
402 C<function> and C<method> is that C<method>s automatically
403 L<shift|perlfunc/shift> their first argument into C<$self>.
405 The following shortcuts are available:
407 use Function::Parameters;
409 use Function::Parameters { fun => 'function', method => 'method' };
415 use Function::Parameters 'foo';
417 use Function::Parameters { 'foo' => 'function' };
423 use Function::Parameters 'foo', 'bar';
425 use Function::Parameters { 'foo' => 'function', 'bar' => 'method' };
427 =head2 Other advanced stuff
429 Normally, Perl subroutines are not in scope in their own body, meaning the
430 parser doesn't know the name C<foo> or its prototype while processing
431 C<sub foo ($) { foo $bar[1], $bar[0]; }>, parsing it as
432 C<$bar-E<gt>foo([1], $bar[0])>. Yes. You can add parens to change the
433 interpretation of this code, but C<foo($bar[1], $bar[0])> will only trigger
434 a I<foo() called too early to check prototype> warning. This module attempts
435 to fix all of this by adding a subroutine declaration before the definition,
436 so the parser knows the name (and possibly prototype) while it processes the
437 body. Thus C<fun foo($x) :($) { $x }> really turns into
438 C<sub foo ($); sub foo ($) { my ($x) = @_; $x }>.
440 If you need L<subroutine attributes|perlsub/"Subroutine Attributes">, you can
441 put them after the parameter list with their usual syntax.
443 Syntactically, these new parameter lists live in the spot normally occupied
444 by L<prototypes|perlsub/"Prototypes">. However, you can include a prototype by
445 specifying it as the first attribute (this is syntactically unambiguous
446 because normal attributes have to start with a letter).
448 If you want to wrap C<Function::Parameters>, you may find C<import_into>
449 helpful. It lets you specify a target package for the syntax magic, as in:
451 package Some::Wrapper;
452 use Function::Parameters ();
455 Function::Parameters::import_into $caller;
456 # or Function::Parameters::import_into $caller, @other_import_args;
459 C<import_into> is not exported by this module, so you have to use a fully
460 qualified name to call it.
464 Lukas Mai, C<< <l.mai at web.de> >>
466 =head1 COPYRIGHT & LICENSE
468 Copyright 2010, 2011 Lukas Mai.
470 This program is free software; you can redistribute it and/or modify it
471 under the terms of either: the GNU General Public License as published
472 by the Free Software Foundation; or the Artistic License.
474 See http://dev.perl.org/licenses/ for more information.