File: | /usr/local/share/automake-1.11/Automake/Struct.pm |
Coverage: | 46.6% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | # autoconf -- create `configure' using m4 macros | ||||||
2 | # Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc. | ||||||
3 | |||||||
4 | # This program is free software; you can redistribute it and/or modify | ||||||
5 | # it under the terms of the GNU General Public License as published by | ||||||
6 | # the Free Software Foundation; either version 2, or (at your option) | ||||||
7 | # any later version. | ||||||
8 | |||||||
9 | # This program is distributed in the hope that it will be useful, | ||||||
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
12 | # GNU General Public License for more details. | ||||||
13 | |||||||
14 | # You should have received a copy of the GNU General Public License | ||||||
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
16 | |||||||
17 | # This file is basically Perl 5.6's Class::Struct, but made compatible | ||||||
18 | # with Perl 5.5. If someday this has to be updated, be sure to rename | ||||||
19 | # all the occurrences of Class::Struct into Automake::Struct, otherwise | ||||||
20 | # if we `use' a Perl module (e.g., File::stat) that uses Class::Struct, | ||||||
21 | # we would have two packages defining the same symbols. Boom. | ||||||
22 | |||||||
23 | ############################################################### | ||||||
24 | # The main copy of this file is in Automake's CVS repository. # | ||||||
25 | # Updates should be sent to automake-patches@gnu.org. # | ||||||
26 | ############################################################### | ||||||
27 | |||||||
28 | package Automake::Struct; | ||||||
29 | |||||||
30 | ## See POD after __END__ | ||||||
31 | |||||||
32 | 1 1 1 | 34 3 1 | use 5.005_03; | ||||
33 | |||||||
34 | 1 1 1 | 4 2 4 | use strict; | ||||
35 | 1 1 1 | 4 1 4 | use vars qw(@ISA @EXPORT $VERSION); | ||||
36 | |||||||
37 | 1 1 1 | 4 1 4 | use Carp; | ||||
38 | |||||||
39 | require Exporter; | ||||||
40 | @ISA = qw(Exporter); | ||||||
41 | @EXPORT = qw(struct); | ||||||
42 | |||||||
43 | $VERSION = '0.58'; | ||||||
44 | |||||||
45 | ## Tested on 5.002 and 5.003 without class membership tests: | ||||||
46 | my $CHECK_CLASS_MEMBERSHIP = ($] >= 5.003_95); | ||||||
47 | |||||||
48 | my $print = 0; | ||||||
49 | sub printem { | ||||||
50 | 0 0 | 0 | 0 0 | if (@_) { $print = shift } | |||
51 | 0 | 0 | else { $print++ } | ||||
52 | } | ||||||
53 | |||||||
54 | { | ||||||
55 | package Automake::Struct::Tie_ISA; | ||||||
56 | |||||||
57 | sub TIEARRAY { | ||||||
58 | 1 | 2 | my $class = shift; | ||||
59 | 1 | 8 | return bless [], $class; | ||||
60 | } | ||||||
61 | |||||||
62 | sub STORE { | ||||||
63 | 0 | 0 | my ($self, $index, $value) = @_; | ||||
64 | 0 | 0 | Automake::Struct::_subclass_error(); | ||||
65 | } | ||||||
66 | |||||||
67 | sub FETCH { | ||||||
68 | 0 | 0 | my ($self, $index) = @_; | ||||
69 | 0 | 0 | $self->[$index]; | ||||
70 | } | ||||||
71 | |||||||
72 | sub FETCHSIZE { | ||||||
73 | 0 | 0 | my $self = shift; | ||||
74 | 0 | 0 | return scalar(@$self); | ||||
75 | } | ||||||
76 | |||||||
77 | 0 | 0 | sub DESTROY { } | ||||
78 | } | ||||||
79 | |||||||
80 | sub struct { | ||||||
81 | |||||||
82 | # Determine parameter list structure, one of: | ||||||
83 | # struct( class => [ element-list ]) | ||||||
84 | # struct( class => { element-list }) | ||||||
85 | # struct( element-list ) | ||||||
86 | # Latter form assumes current package name as struct name. | ||||||
87 | |||||||
88 | 1 | 1 | 2 | my ($class, @decls); | |||
89 | 1 | 3 | my $base_type = ref $_[1]; | ||||
90 | 1 | 5 | if ( $base_type eq 'HASH' ) { | ||||
91 | 0 | 0 | $class = shift; | ||||
92 | 0 0 | 0 0 | @decls = %{shift()}; | ||||
93 | 0 | 0 | _usage_error() if @_; | ||||
94 | } | ||||||
95 | elsif ( $base_type eq 'ARRAY' ) { | ||||||
96 | 0 | 0 | $class = shift; | ||||
97 | 0 0 | 0 0 | @decls = @{shift()}; | ||||
98 | 0 | 0 | _usage_error() if @_; | ||||
99 | } | ||||||
100 | else { | ||||||
101 | 1 | 2 | $base_type = 'ARRAY'; | ||||
102 | 1 | 3 | $class = (caller())[0]; | ||||
103 | 1 | 15 | @decls = @_; | ||||
104 | } | ||||||
105 | 1 | 6 | _usage_error() if @decls % 2 == 1; | ||||
106 | |||||||
107 | # Ensure we are not, and will not be, a subclass. | ||||||
108 | |||||||
109 | 1 | 3 | my $isa = do { | ||||
110 | 1 1 1 | 4 5 2 | no strict 'refs'; | ||||
111 | 1 1 | 1 8 | \@{$class . '::ISA'}; | ||||
112 | }; | ||||||
113 | 1 | 4 | _subclass_error() if @$isa; | ||||
114 | 1 | 1 | tie @$isa, 'Automake::Struct::Tie_ISA'; | ||||
115 | |||||||
116 | # Create constructor. | ||||||
117 | |||||||
118 | croak "function 'new' already defined in package $class" | ||||||
119 | 1 1 1 1 1 1 | 4 1 2 3 1 5 | if do { no strict 'refs'; defined &{$class . "::new"} }; | ||||
120 | |||||||
121 | 1 | 3 | my @methods = (); | ||||
122 | 1 | 2 | my %refs = (); | ||||
123 | 1 | 1 | my %arrays = (); | ||||
124 | 1 | 1 | my %hashes = (); | ||||
125 | 1 | 2 | my %classes = (); | ||||
126 | 1 | 1 | my $got_class = 0; | ||||
127 | 1 | 1 | my $out = ''; | ||||
128 | |||||||
129 | 1 | 3 | $out = "{\n package $class;\n use Carp;\n sub new {\n"; | ||||
130 | 1 | 2 | $out .= " my (\$class, \%init) = \@_;\n"; | ||||
131 | 1 | 2 | $out .= " \$class = __PACKAGE__ unless \@_;\n"; | ||||
132 | |||||||
133 | 1 | 2 | my $cnt = 0; | ||||
134 | 1 | 2 | my $idx = 0; | ||||
135 | 1 | 2 | my( $cmt, $name, $type, $elem ); | ||||
136 | |||||||
137 | 1 | 9 | if( $base_type eq 'HASH' ){ | ||||
138 | 0 | 0 | $out .= " my(\$r) = {};\n"; | ||||
139 | 0 | 0 | $cmt = ''; | ||||
140 | } | ||||||
141 | elsif( $base_type eq 'ARRAY' ){ | ||||||
142 | 1 | 2 | $out .= " my(\$r) = [];\n"; | ||||
143 | } | ||||||
144 | 1 | 3 | while( $idx < @decls ){ | ||||
145 | 23 | 31 | $name = $decls[$idx]; | ||||
146 | 23 | 36 | $type = $decls[$idx+1]; | ||||
147 | 23 | 27 | push( @methods, $name ); | ||||
148 | 23 | 55 | if( $base_type eq 'HASH' ){ | ||||
149 | 0 | 0 | $elem = "{'${class}::$name'}"; | ||||
150 | } | ||||||
151 | elsif( $base_type eq 'ARRAY' ){ | ||||||
152 | 23 | 38 | $elem = "[$cnt]"; | ||||
153 | 23 | 19 | ++$cnt; | ||||
154 | 23 | 25 | $cmt = " # $name"; | ||||
155 | } | ||||||
156 | 23 | 37 | if( $type =~ /^\*(.)/ ){ | ||||
157 | 0 | 0 | $refs{$name}++; | ||||
158 | 0 | 0 | $type = $1; | ||||
159 | } | ||||||
160 | 23 | 45 | my $init = "defined(\$init{'$name'}) ? \$init{'$name'} :"; | ||||
161 | 23 | 63 | if( $type eq '@' ){ | ||||
162 | 3 | 5 | $out .= " croak 'Initializer for $name must be array reference'\n"; | ||||
163 | 3 | 10 | $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'ARRAY';\n"; | ||||
164 | 3 | 8 | $out .= " \$r->$elem = $init [];$cmt\n"; | ||||
165 | 3 | 6 | $arrays{$name}++; | ||||
166 | } | ||||||
167 | elsif( $type eq '%' ){ | ||||||
168 | 0 | 0 | $out .= " croak 'Initializer for $name must be hash reference'\n"; | ||||
169 | 0 | 0 | $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'HASH';\n"; | ||||
170 | 0 | 0 | $out .= " \$r->$elem = $init {};$cmt\n"; | ||||
171 | 0 | 0 | $hashes{$name}++; | ||||
172 | } | ||||||
173 | elsif ( $type eq '$') { | ||||||
174 | 20 | 47 | $out .= " \$r->$elem = $init undef;$cmt\n"; | ||||
175 | } | ||||||
176 | elsif( $type =~ /^\w+(?:::\w+)*$/ ){ | ||||||
177 | 0 | 0 | $init = "defined(\$init{'$name'}) ? \%{\$init{'$name'}} : ()"; | ||||
178 | 0 | 0 | $out .= " croak 'Initializer for $name must be hash reference'\n"; | ||||
179 | 0 | 0 | $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'HASH';\n"; | ||||
180 | 0 | 0 | $out .= " \$r->$elem = '${type}'->new($init);$cmt\n"; | ||||
181 | 0 | 0 | $classes{$name} = $type; | ||||
182 | 0 | 0 | $got_class = 1; | ||||
183 | } | ||||||
184 | else{ | ||||||
185 | 0 | 0 | croak "'$type' is not a valid struct element type"; | ||||
186 | } | ||||||
187 | 23 | 50 | $idx += 2; | ||||
188 | } | ||||||
189 | 1 | 18 | $out .= " bless \$r, \$class;\n }\n"; | ||||
190 | |||||||
191 | # Create accessor methods. | ||||||
192 | |||||||
193 | 1 | 2 | my( $pre, $pst, $sel ); | ||||
194 | 1 | 2 | $cnt = 0; | ||||
195 | 1 | 2 | foreach $name (@methods){ | ||||
196 | 1 1 1 23 23 23 | 4 1 2 21 19 73 | if ( do { no strict 'refs'; defined &{$class . "::$name"} } ) { | ||||
197 | 0 | 0 | carp "function '$name' already defined, overrides struct accessor method"; | ||||
198 | } | ||||||
199 | else { | ||||||
200 | 23 | 40 | $pre = $pst = $cmt = $sel = ''; | ||||
201 | 23 | 43 | if( defined $refs{$name} ){ | ||||
202 | 0 | 0 | $pre = "\\("; | ||||
203 | 0 | 0 | $pst = ")"; | ||||
204 | 0 | 0 | $cmt = " # returns ref"; | ||||
205 | } | ||||||
206 | 23 | 48 | $out .= " sub $name {$cmt\n my \$r = shift;\n"; | ||||
207 | 23 | 38 | if( $base_type eq 'ARRAY' ){ | ||||
208 | 23 | 29 | $elem = "[$cnt]"; | ||||
209 | 23 | 26 | ++$cnt; | ||||
210 | } | ||||||
211 | elsif( $base_type eq 'HASH' ){ | ||||||
212 | 0 | 0 | $elem = "{'${class}::$name'}"; | ||||
213 | } | ||||||
214 | 23 | 74 | if( defined $arrays{$name} ){ | ||||
215 | 3 | 4 | $out .= " my \$i;\n"; | ||||
216 | 3 | 4 | $out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n"; | ||||
217 | 3 | 6 | $sel = "->[\$i]"; | ||||
218 | } | ||||||
219 | elsif( defined $hashes{$name} ){ | ||||||
220 | 0 | 0 | $out .= " my \$i;\n"; | ||||
221 | 0 | 0 | $out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n"; | ||||
222 | 0 | 0 | $sel = "->{\$i}"; | ||||
223 | } | ||||||
224 | elsif( defined $classes{$name} ){ | ||||||
225 | 0 | 0 | if ( $CHECK_CLASS_MEMBERSHIP ) { | ||||
226 | 0 | 0 | $out .= " croak '$name argument is wrong class' if \@_ && ! UNIVERSAL::isa(\$_[0], '$classes{$name}');\n"; | ||||
227 | } | ||||||
228 | } | ||||||
229 | 23 | 34 | $out .= " croak 'Too many args to $name' if \@_ > 1;\n"; | ||||
230 | 23 | 87 | $out .= " \@_ ? ($pre\$r->$elem$sel = shift$pst) : $pre\$r->$elem$sel$pst;\n"; | ||||
231 | 23 | 37 | $out .= " }\n"; | ||||
232 | } | ||||||
233 | } | ||||||
234 | 1 | 2 | $out .= "}\n1;\n"; | ||||
235 | |||||||
236 | 1 | 2 | print $out if $print; | ||||
237 | 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 54 54 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 18 18 18 63 63 63 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 18 0 0 0 45 45 45 0 0 0 0 0 0 0 0 0 | 6 1 7 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 69 44 202 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 13 25 22 33 49 74 125 247 89 31 25 59 41 103 42 39 49 44 51 45 42 89 43 44 85 43 41 37 52 42 47 41 42 41 41 37 44 88 0 0 0 58 88 138 | my $result = eval $out; | ||||
238 | 1 | 8 | carp $@ if $@; | ||||
239 | } | ||||||
240 | |||||||
241 | sub _usage_error { | ||||||
242 | 0 | confess "struct usage error"; | |||||
243 | } | ||||||
244 | |||||||
245 | sub _subclass_error { | ||||||
246 | 0 | croak 'struct class cannot be a subclass (@ISA not allowed)'; | |||||
247 | } | ||||||
248 | |||||||
249 | 1; # for require | ||||||
250 | |||||||
251 |