rewrite in XS
[p5sagit/Function-Parameters.git] / lib / Function / Parameters.pm
CommitLineData
7a63380c 1package Function::Parameters;
2
3use strict;
4use warnings;
5
db81d362 6use XSLoader;
7BEGIN {
8 our $VERSION = '0.05_01';
9 XSLoader::load;
7a63380c 10}
11
db81d362 12use B::Hooks::EndOfScope qw(on_scope_end);
13use Carp qw(confess);
14use bytes ();
7a63380c 15
2d5cf47a 16sub _assert_valid_identifier {
17 my ($name, $with_dollar) = @_;
18 my $bonus = $with_dollar ? '\$' : '';
19 $name =~ /^${bonus}[^\W\d]\w*\z/
20 or confess qq{"$name" doesn't look like a valid identifier};
21}
22
125c067e 23my @bare_arms = qw(function method);
2d5cf47a 24my %type_map = (
25 function => { name => 'optional' },
26 method => { name => 'optional', shift => '$self' },
27);
c9a39f6b 28
db81d362 29sub import {
30 my $class = shift;
7a63380c 31
125c067e 32 @_ or @_ = ('fun', 'method');
33 if (@_ == 1 && ref($_[0]) eq 'HASH') {
34 @_ = map [$_, $_[0]{$_}], keys %{$_[0]}
35 or return;
36 }
7a63380c 37
125c067e 38 my %spec;
39
40 my $bare = 0;
41 for my $proto (@_) {
42 my $item = ref $proto
43 ? $proto
44 : [$proto, $bare_arms[$bare++] || confess(qq{Don't know what to do with "$proto"})]
45 ;
46 my ($name, $type) = @$item;
2d5cf47a 47 _assert_valid_identifier $name;
48
49 unless (ref $type) {
50 # use '||' instead of 'or' to preserve $type in the error message
51 $type = $type_map{$type}
52 || confess qq["$type" doesn't look like a valid type (one of ${\join ', ', sort keys %type_map})];
53 }
54 $type->{name} ||= 'optional';
55 $type->{name} =~ /^(?:optional|required|prohibited)\z/
56 or confess qq["$type->{name}" doesn't look like a valid name attribute (one of optional, required, prohibited)];
db81d362 57 if ($type->{shift}) {
58 _assert_valid_identifier $type->{shift}, 1;
59 bytes::length($type->{shift}) < SHIFT_NAME_LIMIT
60 or confess qq["$type->{shift}" is longer than I can handle];
61 }
125c067e 62
db81d362 63 $spec{$name} = $type;
125c067e 64 }
65
db81d362 66 for my $kw (keys %spec) {
67 my $type = $spec{$kw};
68
69 $^H{HINTK_SHIFT_ . $kw} = $type->{shift} || '';
70 $^H{HINTK_NAME_ . $kw} =
71 $type->{name} eq 'prohibited' ? FLAG_NAME_PROHIBITED :
72 $type->{name} eq 'required' ? FLAG_NAME_REQUIRED :
73 FLAG_NAME_OPTIONAL
74 ;
75 $^H{+HINTK_KEYWORDS} .= "$kw ";
125c067e 76 }
eeb7df5f 77}
78
db81d362 79sub unimport {
eeb7df5f 80 my $class = shift;
125c067e 81
db81d362 82 if (!@_) {
83 delete $^H{+HINTK_KEYWORDS};
125c067e 84 return;
85 }
86
db81d362 87 for my $kw (@_) {
88 $^H{+HINTK_KEYWORDS} =~ s/(?<![^ ])\Q$kw\E //g;
125c067e 89 }
90}
91
125c067e 92sub _fini {
7a63380c 93 on_scope_end {
db81d362 94 xs_fini;
7a63380c 95 };
96}
97
db81d362 98
125c067e 99'ok'
7a63380c 100
101__END__
102
103=head1 NAME
104
105Function::Parameters - subroutine definitions with parameter lists
106
107=head1 SYNOPSIS
108
109 use Function::Parameters;
110
111 fun foo($bar, $baz) {
112 return $bar + $baz;
113 }
114
115 fun mymap($fun, @args) :(&@) {
116 my @res;
117 for (@args) {
118 push @res, $fun->($_);
119 }
120 @res
121 }
122
123 print "$_\n" for mymap { $_ * 2 } 1 .. 4;
125c067e 124
125 method set_name($name) {
126 $self->{name} = $name;
127 }
7a63380c 128
125c067e 129=cut
130
131=pod
132
133 use Function::Parameters 'proc', 'meth';
c9a39f6b 134
125c067e 135 my $f = proc ($x) { $x * 2 };
136 meth get_age() {
137 return $self->{age};
138 }
139
7a63380c 140=head1 DESCRIPTION
141
142This module lets you use parameter lists in your subroutines. Thanks to
db81d362 143L<perlapi/PL_keyword_plugin> it works without source filters.
7a63380c 144
db81d362 145WARNING: This is my first attempt at writing L<XS code|perlxs> and I have
7a63380c 146almost no experience with perl's internals. So while this module might
147appear to work, it could also conceivably make your programs segfault.
148Consider this module alpha quality.
149
150=head2 Basic stuff
151
152To use this new functionality, you have to use C<fun> instead of C<sub> -
153C<sub> continues to work as before. The syntax is almost the same as for
154C<sub>, but after the subroutine name (or directly after C<fun> if you're
125c067e 155writing an anonymous sub) you can write a parameter list in parentheses. This
7a63380c 156list consists of comma-separated variables.
157
158The effect of C<fun foo($bar, $baz) {> is as if you'd written
159C<sub foo { my ($bar, $baz) = @_; >, i.e. the parameter list is simply
160copied into C<my> and initialized from L<@_|perlvar/"@_">.
161
125c067e 162In addition you can use C<method>, which understands the same syntax as C<fun>
163but automatically creates a C<$self> variable for you. So by writing
164C<method foo($bar, $baz) {> you get the same effect as
165C<sub foo { my $self = shift; my ($bar, $baz) = @_; >.
7a63380c 166
125c067e 167=head2 Customizing the generated keywords
c9a39f6b 168
125c067e 169You can customize the names of the keywords injected in your package. To do that
170you pass a hash reference in the import list:
7a63380c 171
125c067e 172 use Function::Parameters { proc => 'function', meth => 'method' }; # -or-
173 use Function::Parameters { proc => 'function' }; # -or-
174 use Function::Parameters { meth => 'method' };
175
176The first line creates two keywords, C<proc> and C<meth> (for defining
177functions and methods, respectively). The last two lines only create one
178keyword. Generally the hash keys can be any identifiers you want while the
ce052c57 179values have to be either C<function>, C<method>, or a hash reference (see
180below). The difference between C<function> and C<method> is that C<method>s
181automatically L<shift|perlfunc/shift> their first argument into C<$self>.
125c067e 182
183The following shortcuts are available:
184
185 use Function::Parameters;
186 # is equivalent to #
187 use Function::Parameters { fun => 'function', method => 'method' };
188
189=cut
190
191=pod
192
193 use Function::Parameters 'foo';
194 # is equivalent to #
195 use Function::Parameters { 'foo' => 'function' };
196
197=cut
198
199=pod
200
201 use Function::Parameters 'foo', 'bar';
202 # is equivalent to #
203 use Function::Parameters { 'foo' => 'function', 'bar' => 'method' };
204
ce052c57 205You can customize things even more by passing a hashref instead of C<function>
206or C<method>. This hash can have the following keys:
207
208=over
209
210=item C<name>
211
212Valid values: C<optional> (default), C<required> (all uses of this keyword must
213specify a function name), and C<prohibited> (all uses of this keyword must not
214specify a function name). This means a C<< name => 'prohibited' >> keyword can
215only be used for defining anonymous functions.
216
217=item C<shift>
218
219Valid values: strings that look like a scalar variable. Any function created by
220this keyword will automatically L<shift|perlfunc/shift> its first argument into
221a local variable with the name specified here.
222
223=back
224
225Plain C<function> is equivalent to C<< { name => 'optional' } >>, and plain
226C<method> is equivalent to C<< { name => 'optional', shift => '$self'} >>.
227
125c067e 228=head2 Other advanced stuff
7a63380c 229
230Normally, Perl subroutines are not in scope in their own body, meaning the
125c067e 231parser doesn't know the name C<foo> or its prototype while processing
7a63380c 232C<sub foo ($) { foo $bar[1], $bar[0]; }>, parsing it as
233C<$bar-E<gt>foo([1], $bar[0])>. Yes. You can add parens to change the
234interpretation of this code, but C<foo($bar[1], $bar[0])> will only trigger
235a I<foo() called too early to check prototype> warning. This module attempts
236to fix all of this by adding a subroutine declaration before the definition,
237so the parser knows the name (and possibly prototype) while it processes the
238body. Thus C<fun foo($x) :($) { $x }> really turns into
239C<sub foo ($); sub foo ($) { my ($x) = @_; $x }>.
240
125c067e 241If you need L<subroutine attributes|perlsub/"Subroutine Attributes">, you can
242put them after the parameter list with their usual syntax.
243
244Syntactically, these new parameter lists live in the spot normally occupied
245by L<prototypes|perlsub/"Prototypes">. However, you can include a prototype by
246specifying it as the first attribute (this is syntactically unambiguous
247because normal attributes have to start with a letter).
248
db81d362 249If you want to wrap L<Function::Parameters>, you just have to call its
250C<import> method. It always applies to the file that is currently being parsed
251and its effects are lexical (i.e. it works like L<warnings> or L<strict>);
eeb7df5f 252
253 package Some::Wrapper;
254 use Function::Parameters ();
255 sub import {
db81d362 256 Function::Parameters->import;
257 # or Function::Parameters->import(@other_import_args);
eeb7df5f 258 }
259
7a63380c 260=head1 AUTHOR
261
262Lukas Mai, C<< <l.mai at web.de> >>
263
264=head1 COPYRIGHT & LICENSE
265
db81d362 266Copyright 2010, 2011, 2012 Lukas Mai.
7a63380c 267
268This program is free software; you can redistribute it and/or modify it
269under the terms of either: the GNU General Public License as published
270by the Free Software Foundation; or the Artistic License.
271
272See http://dev.perl.org/licenses/ for more information.
273
274=cut