Make devel::declare parse the part between prototype and
[p5sagit/Devel-Declare.git] / lib / Devel / Declare.pm
CommitLineData
94caac6e 1package Devel::Declare;
2
3use strict;
4use warnings;
5use 5.008001;
6
498cc8bc 7our $VERSION = '0.001006';
94caac6e 8
0ba8c7aa 9# mirrored in Declare.xs as DD_HANDLE_*
10
11use constant DECLARE_NAME => 1;
12use constant DECLARE_PROTO => 2;
53e3ab32 13use constant DECLARE_NONE => 4;
15d0d014 14use constant DECLARE_PACKAGE => 8+1; # name implicit
0ba8c7aa 15
86c3de80 16use vars qw(%declarators %declarator_handlers @ISA);
94caac6e 17use base qw(DynaLoader);
323ae557 18use Scalar::Util 'set_prototype';
94caac6e 19
20bootstrap Devel::Declare;
21
86c3de80 22@ISA = ();
23
94caac6e 24sub import {
0ba8c7aa 25 my ($class, %args) = @_;
94caac6e 26 my $target = caller;
0ba8c7aa 27 if (@_ == 1) { # "use Devel::Declare;"
28 no strict 'refs';
15d0d014 29 foreach my $name (qw(NAME PROTO NONE PACKAGE)) {
53e3ab32 30 *{"${target}::DECLARE_${name}"} = *{"DECLARE_${name}"};
0ba8c7aa 31 }
32 } else {
33 $class->setup_for($target => \%args);
34 }
94caac6e 35}
36
37sub unimport {
38 my ($class) = @_;
39 my $target = caller;
40 $class->teardown_for($target);
41}
42
43sub setup_for {
44 my ($class, $target, $args) = @_;
45 setup();
0ba8c7aa 46 foreach my $key (keys %$args) {
47 my $info = $args->{$key};
48 my ($flags, $sub);
49 if (ref($info) eq 'ARRAY') {
50 ($flags, $sub) = @$info;
51 } elsif (ref($info) eq 'CODE') {
52 $flags = DECLARE_NAME;
53 $sub = $info;
54 } else {
55 die "Info for sub ${key} must be [ \$flags, \$sub ] or \$sub";
56 }
57 $declarators{$target}{$key} = $flags;
58 $declarator_handlers{$target}{$key} = $sub;
59 }
94caac6e 60}
61
62sub teardown_for {
63 my ($class, $target) = @_;
64 delete $declarators{$target};
0ba8c7aa 65 delete $declarator_handlers{$target};
94caac6e 66 teardown();
67}
68
94caac6e 69my $temp_name;
0ba8c7aa 70my $temp_save;
94caac6e 71
72sub init_declare {
0f070758 73 my ($usepack, $use, $inpack, $name, $proto, $traits) = @_;
53e3ab32 74 my ($name_h, $XX_h, $extra_code)
9026391e 75 = $declarator_handlers{$usepack}{$use}->(
0f070758 76 $usepack, $use, $inpack, $name, $proto, defined(wantarray), $traits
53e3ab32 77 );
15d0d014 78 ($temp_name, $temp_save) = ([], []);
0ba8c7aa 79 if ($name) {
9026391e 80 $name = "${inpack}::${name}" unless $name =~ /::/;
0ba8c7aa 81 push(@$temp_name, $name);
82 no strict 'refs';
15d0d014 83 push(@$temp_save, \&{$name});
0ba8c7aa 84 no warnings 'redefine';
85 no warnings 'prototype';
15d0d014 86 *{$name} = $name_h;
0ba8c7aa 87 }
88 if ($XX_h) {
9026391e 89 push(@$temp_name, "${inpack}::X");
0ba8c7aa 90 no strict 'refs';
9026391e 91 push(@$temp_save, \&{"${inpack}::X"});
0ba8c7aa 92 no warnings 'redefine';
93 no warnings 'prototype';
9026391e 94 *{"${inpack}::X"} = $XX_h;
0ba8c7aa 95 }
53e3ab32 96 if (defined wantarray) {
97 return $extra_code || '0;';
98 } else {
99 return;
100 }
94caac6e 101}
102
103sub done_declare {
104 no strict 'refs';
86c3de80 105 my $name = shift(@{$temp_name||[]});
0ba8c7aa 106 die "done_declare called with no temp_name stack" unless defined($name);
86c3de80 107 my $saved = shift(@$temp_save);
15d0d014 108 $name =~ s/(.*):://;
109 my $temp_pack = $1;
0ba8c7aa 110 delete ${"${temp_pack}::"}{$name};
111 if ($saved) {
112 no warnings 'prototype';
113 *{"${temp_pack}::${name}"} = $saved;
114 }
94caac6e 115}
116
323ae557 117sub build_sub_installer {
118 my ($class, $pack, $name, $proto) = @_;
119 return eval "
120 package ${pack};
121 my \$body;
122 sub ${name} (${proto}) :lvalue {\n"
003ac394 123 .' if (wantarray) {
c5912dc7 124 goto &$body;
003ac394 125 }
126 my $ret = $body->(@_);
86c3de80 127 return $ret;
323ae557 128 };
129 sub { ($body) = @_; };';
130}
131
132sub setup_declarators {
133 my ($class, $pack, $to_setup) = @_;
86c3de80 134 die "${class}->setup_declarators(\$pack, \\\%to_setup)"
135 unless defined($pack) && ref($to_setup) eq 'HASH';
136 my %setup_for_args;
323ae557 137 foreach my $name (keys %$to_setup) {
138 my $info = $to_setup->{$name};
139 my $flags = $info->{flags} || DECLARE_NAME;
140 my $run = $info->{run};
141 my $compile = $info->{compile};
142 my $proto = $info->{proto} || '&';
143 my $sub_proto = $proto;
144 # make all args optional to enable lvalue for DECLARE_NONE
145 $sub_proto =~ s/;//; $sub_proto = ';'.$sub_proto;
86c3de80 146 #my $installer = $class->build_sub_installer($pack, $name, $proto);
147 my $installer = $class->build_sub_installer($pack, $name, '@');
86c3de80 148 $installer->(sub :lvalue {
003ac394 149#{ no warnings 'uninitialized'; warn 'INST: '.join(', ', @_)."\n"; }
c5534496 150 if (@_) {
151 if (ref $_[0] eq 'HASH') {
152 shift;
003ac394 153 if (wantarray) {
154 my @ret = $run->(undef, undef, @_);
155 return @ret;
156 }
c5534496 157 my $r = $run->(undef, undef, @_);
158 return $r;
159 } else {
003ac394 160 return @_[1..$#_];
c5534496 161 }
86c3de80 162 }
163 return my $sv;
164 });
165 $setup_for_args{$name} = [
166 $flags,
167 sub {
0f070758 168 my ($usepack, $use, $inpack, $name, $proto, $shift_hashref, $traits) = @_;
169 my $extra_code = $compile->($name, $proto, $traits);
003ac394 170 my $main_handler = sub { shift if $shift_hashref;
c5534496 171 ("DONE", $run->($name, $proto, @_));
003ac394 172 };
86c3de80 173 my ($name_h, $XX);
174 if (defined $proto) {
175 $name_h = sub :lvalue { return my $sv; };
176 $XX = $main_handler;
c5534496 177 } elsif (defined $name && length $name) {
86c3de80 178 $name_h = $main_handler;
179 }
003ac394 180 $extra_code ||= '';
181 $extra_code = '}, sub {'.$extra_code;
86c3de80 182 return ($name_h, $XX, $extra_code);
183 }
184 ];
323ae557 185 }
86c3de80 186 $class->setup_for($pack, \%setup_for_args);
187}
188
189sub install_declarator {
190 my ($class, $target_pack, $target_name, $flags, $filter, $handler) = @_;
191 $class->setup_declarators($target_pack, {
192 $target_name => {
193 flags => $flags,
194 compile => $filter,
195 run => $handler,
196 }
197 });
323ae557 198}
199
94caac6e 200=head1 NAME
201
202Devel::Declare -
203
204=head1 SYNOPSIS
205
f5f9f113 206Look at the tests. This module is currently on CPAN to ease smoke testing
207and allow early adopters who've been involved in the design to experiment
208with it.
209
94caac6e 210=head1 DESCRIPTION
211
212=head2 import
213
214 use Devel::Declare qw(list of subs);
215
216Calls Devel::Declare->setup_for(__PACKAGE__ => \@list_of_subs);
217
218=head2 unimport
219
220 no Devel::Declare;
221
222Calls Devel::Declare->teardown_for(__PACKAGE__);
223
224=head2 setup_for
225
226 Devel::Declare->setup_for($package => \@subnames);
227
228Installs declarator magic (unless already installed) and registers
229"${package}::$name" for each member of @subnames
230
231=head2 teardown_for
232
233 Devel::Declare->teardown_for($package);
234
235Deregisters all subs currently registered for $package and uninstalls
236declarator magic if number of teardown_for calls matches number of setup_for
237calls.
238
239=head1 AUTHOR
240
02f5a508 241Matt S Trout - <mst@shadowcat.co.uk>
94caac6e 242
02f5a508 243Company: http://www.shadowcat.co.uk/
94caac6e 244Blog: http://chainsawblues.vox.com/
245
246=head1 LICENSE
247
248This library is free software under the same terms as perl itself
249
250=cut
251
2521;