initial working perl-space version
[p5sagit/Devel-Declare.git] / lib / Devel / Declare.pm
CommitLineData
94caac6e 1package Devel::Declare;
2
3use strict;
4use warnings;
5use 5.008001;
6
bedac9ff 7our $VERSION = '0.001011';
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}
67
94caac6e 68my $temp_name;
0ba8c7aa 69my $temp_save;
94caac6e 70
71sub init_declare {
0f070758 72 my ($usepack, $use, $inpack, $name, $proto, $traits) = @_;
53e3ab32 73 my ($name_h, $XX_h, $extra_code)
9026391e 74 = $declarator_handlers{$usepack}{$use}->(
0f070758 75 $usepack, $use, $inpack, $name, $proto, defined(wantarray), $traits
53e3ab32 76 );
15d0d014 77 ($temp_name, $temp_save) = ([], []);
0ba8c7aa 78 if ($name) {
9026391e 79 $name = "${inpack}::${name}" unless $name =~ /::/;
0ba8c7aa 80 push(@$temp_name, $name);
81 no strict 'refs';
15d0d014 82 push(@$temp_save, \&{$name});
0ba8c7aa 83 no warnings 'redefine';
84 no warnings 'prototype';
15d0d014 85 *{$name} = $name_h;
0ba8c7aa 86 }
87 if ($XX_h) {
9026391e 88 push(@$temp_name, "${inpack}::X");
0ba8c7aa 89 no strict 'refs';
9026391e 90 push(@$temp_save, \&{"${inpack}::X"});
0ba8c7aa 91 no warnings 'redefine';
92 no warnings 'prototype';
9026391e 93 *{"${inpack}::X"} = $XX_h;
0ba8c7aa 94 }
53e3ab32 95 if (defined wantarray) {
96 return $extra_code || '0;';
97 } else {
98 return;
99 }
94caac6e 100}
101
102sub done_declare {
103 no strict 'refs';
86c3de80 104 my $name = shift(@{$temp_name||[]});
0ba8c7aa 105 die "done_declare called with no temp_name stack" unless defined($name);
86c3de80 106 my $saved = shift(@$temp_save);
15d0d014 107 $name =~ s/(.*):://;
108 my $temp_pack = $1;
0ba8c7aa 109 delete ${"${temp_pack}::"}{$name};
110 if ($saved) {
111 no warnings 'prototype';
112 *{"${temp_pack}::${name}"} = $saved;
113 }
94caac6e 114}
115
323ae557 116sub build_sub_installer {
117 my ($class, $pack, $name, $proto) = @_;
118 return eval "
119 package ${pack};
120 my \$body;
121 sub ${name} (${proto}) :lvalue {\n"
003ac394 122 .' if (wantarray) {
c5912dc7 123 goto &$body;
003ac394 124 }
125 my $ret = $body->(@_);
86c3de80 126 return $ret;
323ae557 127 };
128 sub { ($body) = @_; };';
129}
130
131sub setup_declarators {
132 my ($class, $pack, $to_setup) = @_;
86c3de80 133 die "${class}->setup_declarators(\$pack, \\\%to_setup)"
134 unless defined($pack) && ref($to_setup) eq 'HASH';
135 my %setup_for_args;
323ae557 136 foreach my $name (keys %$to_setup) {
137 my $info = $to_setup->{$name};
138 my $flags = $info->{flags} || DECLARE_NAME;
139 my $run = $info->{run};
140 my $compile = $info->{compile};
141 my $proto = $info->{proto} || '&';
142 my $sub_proto = $proto;
143 # make all args optional to enable lvalue for DECLARE_NONE
144 $sub_proto =~ s/;//; $sub_proto = ';'.$sub_proto;
86c3de80 145 #my $installer = $class->build_sub_installer($pack, $name, $proto);
146 my $installer = $class->build_sub_installer($pack, $name, '@');
86c3de80 147 $installer->(sub :lvalue {
003ac394 148#{ no warnings 'uninitialized'; warn 'INST: '.join(', ', @_)."\n"; }
c5534496 149 if (@_) {
150 if (ref $_[0] eq 'HASH') {
151 shift;
003ac394 152 if (wantarray) {
153 my @ret = $run->(undef, undef, @_);
154 return @ret;
155 }
c5534496 156 my $r = $run->(undef, undef, @_);
157 return $r;
158 } else {
003ac394 159 return @_[1..$#_];
c5534496 160 }
86c3de80 161 }
162 return my $sv;
163 });
164 $setup_for_args{$name} = [
165 $flags,
166 sub {
0f070758 167 my ($usepack, $use, $inpack, $name, $proto, $shift_hashref, $traits) = @_;
168 my $extra_code = $compile->($name, $proto, $traits);
003ac394 169 my $main_handler = sub { shift if $shift_hashref;
c5534496 170 ("DONE", $run->($name, $proto, @_));
003ac394 171 };
86c3de80 172 my ($name_h, $XX);
173 if (defined $proto) {
174 $name_h = sub :lvalue { return my $sv; };
175 $XX = $main_handler;
c5534496 176 } elsif (defined $name && length $name) {
86c3de80 177 $name_h = $main_handler;
178 }
003ac394 179 $extra_code ||= '';
180 $extra_code = '}, sub {'.$extra_code;
86c3de80 181 return ($name_h, $XX, $extra_code);
182 }
183 ];
323ae557 184 }
86c3de80 185 $class->setup_for($pack, \%setup_for_args);
186}
187
188sub install_declarator {
189 my ($class, $target_pack, $target_name, $flags, $filter, $handler) = @_;
190 $class->setup_declarators($target_pack, {
191 $target_name => {
192 flags => $flags,
193 compile => $filter,
194 run => $handler,
195 }
196 });
323ae557 197}
198
04a8a223 199sub linestr_callback_rv2cv {
200 my ($name, $offset) = @_;
201 $offset += toke_move_past_token($offset);
202 my $pack = get_curstash_name();
203 my $flags = $declarators{$pack}{$name};
204 my ($found_name, $found_proto);
205 my $in_declare = 0;
206 if ($flags & DECLARE_NAME) {
207 $offset += toke_skipspace($offset);
208 my $linestr = get_linestr();
209 if (substr($linestr, $offset, 2) eq '::') {
210 substr($linestr, $offset, 2) = '';
211 set_linestr($linestr);
212 }
213 if (my $len = toke_scan_word($offset, $flags & DECLARE_PACKAGE)) {
214 $found_name = substr($linestr, $offset, $len);
215 $offset += $len;
216 $in_declare++;
217 }
218 }
219 if ($flags & DECLARE_PROTO) {
220 $offset += toke_skipspace($offset);
221 my $linestr = get_linestr();
222 if (substr($linestr, $offset, 1) eq '(') {
223 my $length = toke_scan_str($offset);
224 $found_proto = get_lex_stuff();
225 clear_lex_stuff();
226 my $replace =
227 ($found_name ? ' ' : '=')
228 .'X'.(' ' x length($found_proto));
229 $linestr = get_linestr();
230 substr($linestr, $offset, $length) = $replace;
231 set_linestr($linestr);
232 $offset += $length;
233 $in_declare++;
234 }
235 }
236 my @args = ($pack, $name, $pack, $found_name, $found_proto);
237 set_in_declare($in_declare);
238 $offset += toke_skipspace($offset);
239 my $linestr = get_linestr();
240 if (substr($linestr, $offset, 1) eq '{') {
241 my $ret = init_declare(@args);
242 $offset++;
243 if (defined $ret && length $ret) {
244 substr($linestr, $offset, 0) = $ret;
245 set_linestr($linestr);
246 }
247 } else {
248 init_declare(@args);
249 }
250 #warn "linestr now ${linestr}";
251}
252
569ac469 253sub linestr_callback_const {
04a8a223 254 my ($name, $offset) = @_;
255 my $pack = get_curstash_name();
256 my $flags = $declarators{$pack}{$name};
257 if ($flags & DECLARE_NAME) {
258 $offset += toke_move_past_token($offset);
259 $offset += toke_skipspace($offset);
260 if (toke_scan_word($offset, $flags & DECLARE_PACKAGE)) {
261 my $linestr = get_linestr();
262 substr($linestr, $offset, 0) = '::';
263 set_linestr($linestr);
264 }
265 }
569ac469 266}
267
268sub linestr_callback {
269 my $type = shift;
270 my $meth = "linestr_callback_${type}";
271 __PACKAGE__->can($meth)->(@_);
569ac469 272}
273
94caac6e 274=head1 NAME
275
276Devel::Declare -
277
278=head1 SYNOPSIS
279
f5f9f113 280Look at the tests. This module is currently on CPAN to ease smoke testing
281and allow early adopters who've been involved in the design to experiment
282with it.
283
94caac6e 284=head1 DESCRIPTION
285
286=head2 import
287
288 use Devel::Declare qw(list of subs);
289
290Calls Devel::Declare->setup_for(__PACKAGE__ => \@list_of_subs);
291
292=head2 unimport
293
294 no Devel::Declare;
295
296Calls Devel::Declare->teardown_for(__PACKAGE__);
297
298=head2 setup_for
299
300 Devel::Declare->setup_for($package => \@subnames);
301
302Installs declarator magic (unless already installed) and registers
303"${package}::$name" for each member of @subnames
304
305=head2 teardown_for
306
307 Devel::Declare->teardown_for($package);
308
309Deregisters all subs currently registered for $package and uninstalls
310declarator magic if number of teardown_for calls matches number of setup_for
311calls.
312
313=head1 AUTHOR
314
02f5a508 315Matt S Trout - <mst@shadowcat.co.uk>
94caac6e 316
02f5a508 317Company: http://www.shadowcat.co.uk/
94caac6e 318Blog: http://chainsawblues.vox.com/
319
320=head1 LICENSE
321
322This library is free software under the same terms as perl itself
323
324=cut
325
3261;