Mercurial > repos > jjohnson > crest
comparison enum.pm @ 0:acc8d8bfeb9a
Uploaded
author | jjohnson |
---|---|
date | Wed, 08 Feb 2012 16:59:24 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:acc8d8bfeb9a |
---|---|
1 package enum; | |
2 use strict; | |
3 no strict 'refs'; # Let's just make this very clear right off | |
4 | |
5 use Carp; | |
6 use vars qw($VERSION); | |
7 $VERSION = do { my @r = (q$Revision: 1.16 $ =~ /\d+/g); sprintf '%d.%03d'.'%02d' x ($#r-1), @r}; | |
8 | |
9 my $Ident = '[^\W_0-9]\w*'; | |
10 | |
11 sub ENUM () { 1 } | |
12 sub BITMASK () { 2 } | |
13 | |
14 sub import { | |
15 my $class = shift; | |
16 @_ or return; # Ignore 'use enum;' | |
17 my $pkg = caller() . '::'; | |
18 my $prefix = ''; # default no prefix | |
19 my $index = 0; # default start index | |
20 my $mode = ENUM; # default to enum | |
21 | |
22 ## Pragmas should be as fast as they can be, so we inline some | |
23 ## pieces. | |
24 foreach (@_) { | |
25 ## Plain tag is most common case | |
26 if (/^$Ident$/o) { | |
27 my $n = $index; | |
28 | |
29 if ($mode == ENUM) { | |
30 $index++; | |
31 } | |
32 elsif ($mode == BITMASK) { | |
33 $index ||= 1; | |
34 $index *= 2; | |
35 if ( $index & ($index - 1) ) { | |
36 croak ( | |
37 "$index is not a valid single bitmask " | |
38 . " (Maybe you overflowed your system's max int value?)" | |
39 ); | |
40 } | |
41 } | |
42 else { | |
43 confess qq(Can't Happen: mode $mode invalid); | |
44 } | |
45 | |
46 *{"$pkg$prefix$_"} = sub () { $n }; | |
47 } | |
48 | |
49 ## Index change | |
50 elsif (/^($Ident)=(-?)(.+)$/o) { | |
51 my $name= $1; | |
52 my $neg = $2; | |
53 $index = $3; | |
54 | |
55 ## Convert non-decimal numerics to decimal | |
56 if ($index =~ /^0x[\da-f]+$/i) { ## Hex | |
57 $index = hex $index; | |
58 } | |
59 elsif ($index =~ /^0\d/) { ## Octal | |
60 $index = oct $index; | |
61 } | |
62 elsif ($index !~ /[^\d_]/) { ## 123_456 notation | |
63 $index =~ s/_//g; | |
64 } | |
65 | |
66 ## Force numeric context, but only in numeric context | |
67 if ($index =~ /\D/) { | |
68 $index = "$neg$index"; | |
69 } | |
70 else { | |
71 $index = "$neg$index"; | |
72 $index += 0; | |
73 } | |
74 | |
75 my $n = $index; | |
76 | |
77 if ($mode == BITMASK) { | |
78 ($index & ($index - 1)) | |
79 and croak "$index is not a valid single bitmask"; | |
80 $index *= 2; | |
81 } | |
82 elsif ($mode == ENUM) { | |
83 $index++; | |
84 } | |
85 else { | |
86 confess qq(Can't Happen: mode $mode invalid); | |
87 } | |
88 | |
89 *{"$pkg$prefix$name"} = sub () { $n }; | |
90 } | |
91 | |
92 ## Prefix/option change | |
93 elsif (/^([A-Z]*):($Ident)?(=?)(-?)(.*)/) { | |
94 ## Option change | |
95 if ($1) { | |
96 if ($1 eq 'ENUM') { $mode = ENUM; $index = 0 } | |
97 elsif ($1 eq 'BITMASK') { $mode = BITMASK; $index = 1 } | |
98 else { croak qq(Invalid enum option '$1') } | |
99 } | |
100 | |
101 my $neg = $4; | |
102 | |
103 ## Index change too? | |
104 if ($3) { | |
105 if (length $5) { | |
106 $index = $5; | |
107 | |
108 ## Convert non-decimal numerics to decimal | |
109 if ($index =~ /^0x[\da-f]+$/i) { ## Hex | |
110 $index = hex $index; | |
111 } | |
112 elsif ($index =~ /^0\d/) { ## Oct | |
113 $index = oct $index; | |
114 } | |
115 elsif ($index !~ /[^\d_]/) { ## 123_456 notation | |
116 $index =~ s/_//g; | |
117 } | |
118 | |
119 ## Force numeric context, but only in numeric context | |
120 if ($index =~ /\D/) { | |
121 $index = "$neg$index"; | |
122 } | |
123 else { | |
124 $index = "$neg$index"; | |
125 $index += 0; | |
126 } | |
127 | |
128 ## Bitmask mode must check index changes | |
129 if ($mode == BITMASK) { | |
130 ($index & ($index - 1)) | |
131 and croak "$index is not a valid single bitmask"; | |
132 } | |
133 } | |
134 else { | |
135 croak qq(No index value defined after "="); | |
136 } | |
137 } | |
138 | |
139 ## Incase it's a null prefix | |
140 $prefix = defined $2 ? $2 : ''; | |
141 } | |
142 | |
143 ## A..Z case magic lists | |
144 elsif (/^($Ident)\.\.($Ident)$/o) { | |
145 ## Almost never used, so check last | |
146 foreach my $name ("$1" .. "$2") { | |
147 my $n = $index; | |
148 | |
149 if ($mode == BITMASK) { | |
150 ($index & ($index - 1)) | |
151 and croak "$index is not a valid single bitmask"; | |
152 $index *= 2; | |
153 } | |
154 elsif ($mode == ENUM) { | |
155 $index++; | |
156 } | |
157 else { | |
158 confess qq(Can't Happen: mode $mode invalid); | |
159 } | |
160 | |
161 *{"$pkg$prefix$name"} = sub () { $n }; | |
162 } | |
163 } | |
164 | |
165 else { | |
166 croak qq(Can't define "$_" as enum type (name contains invalid characters)); | |
167 } | |
168 } | |
169 } | |
170 | |
171 1; | |
172 | |
173 __END__ | |
174 | |
175 | |
176 =head1 NAME | |
177 | |
178 enum - C style enumerated types and bitmask flags in Perl | |
179 | |
180 =head1 SYNOPSIS | |
181 | |
182 use enum qw(Sun Mon Tue Wed Thu Fri Sat); | |
183 # Sun == 0, Mon == 1, etc | |
184 | |
185 use enum qw(Forty=40 FortyOne Five=5 Six Seven); | |
186 # Yes, you can change the start indexs at any time as in C | |
187 | |
188 use enum qw(:Prefix_ One Two Three); | |
189 ## Creates Prefix_One, Prefix_Two, Prefix_Three | |
190 | |
191 use enum qw(:Letters_ A..Z); | |
192 ## Creates Letters_A, Letters_B, Letters_C, ... | |
193 | |
194 use enum qw( | |
195 :Months_=0 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec | |
196 :Days_=0 Sun Mon Tue Wed Thu Fri Sat | |
197 :Letters_=20 A..Z | |
198 ); | |
199 ## Prefixes can be changed mid list and can have index changes too | |
200 | |
201 use enum qw(BITMASK:LOCK_ SH EX NB UN); | |
202 ## Creates bitmask constants for LOCK_SH == 1, LOCK_EX == 2, | |
203 ## LOCK_NB == 4, and LOCK_UN == 8. | |
204 ## NOTE: This example is only valid on FreeBSD-2.2.5 however, so don't | |
205 ## actually do this. Import from Fnctl instead. | |
206 | |
207 =head1 DESCRIPTION | |
208 | |
209 Defines a set of symbolic constants with ordered numeric values ala B<C> B<enum> types. | |
210 | |
211 Now capable of creating creating ordered bitmask constants as well. See the B<BITMASKS> | |
212 section for details. | |
213 | |
214 What are they good for? Typical uses would be for giving mnemonic names to indexes of | |
215 arrays. Such arrays might be a list of months, days, or a return value index from | |
216 a function such as localtime(): | |
217 | |
218 use enum qw( | |
219 :Months_=0 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec | |
220 :Days_=0 Sun Mon Tue Wed Thu Fri Sat | |
221 :LC_=0 Sec Min Hour MDay Mon Year WDay YDay Isdst | |
222 ); | |
223 | |
224 if ((localtime)[LC_Mon] == Months_Jan) { | |
225 print "It's January!\n"; | |
226 } | |
227 if ((localtime)[LC_WDay] == Days_Fri) { | |
228 print "It's Friday!\n"; | |
229 } | |
230 | |
231 This not only reads easier, but can also be typo-checked at compile time when | |
232 run under B<use strict>. That is, if you misspell B<Days_Fri> as B<Days_Fry>, | |
233 you'll generate a compile error. | |
234 | |
235 =head1 BITMASKS, bitwise operations, and bitmask option values | |
236 | |
237 The B<BITMASK> option allows the easy creation of bitmask constants such as | |
238 functions like flock() and sysopen() use. These are also very useful for your | |
239 own code as they allow you to efficiently store many true/false options within | |
240 a single integer. | |
241 | |
242 use enum qw(BITMASK: MY_ FOO BAR CAT DOG); | |
243 | |
244 my $foo = 0; | |
245 $foo |= MY_FOO; | |
246 $foo |= MY_DOG; | |
247 | |
248 if ($foo & MY_DOG) { | |
249 print "foo has the MY_DOG option set\n"; | |
250 } | |
251 if ($foo & (MY_BAR | MY_DOG)) { | |
252 print "foo has either the MY_BAR or MY_DOG option set\n" | |
253 } | |
254 | |
255 $foo ^= MY_DOG; ## Turn MY_DOG option off (set its bit to false) | |
256 | |
257 When using bitmasks, remember that you must use the bitwise operators, B<|>, B<&>, B<^>, | |
258 and B<~>. If you try to do an operation like C<$foo += MY_DOG;> and the B<MY_DOG> bit | |
259 has already been set, you'll end up setting other bits you probably didn't want to set. | |
260 You'll find the documentation for these operators in the B<perlop> manpage. | |
261 | |
262 You can set a starting index for bitmasks just as you can for normal B<enum> values, | |
263 but if the given index isn't a power of 2 it won't resolve to a single bit and therefor | |
264 will generate a compile error. Because of this, whenever you set the B<BITFIELD:> | |
265 directive, the index is automatically set to 1. If you wish to go back to normal B<enum> | |
266 mode, use the B<ENUM:> directive. Similarly to the B<BITFIELD> directive, the B<ENUM:> | |
267 directive resets the index to 0. Here's an example: | |
268 | |
269 use enum qw( | |
270 BITMASK:BITS_ FOO BAR CAT DOG | |
271 ENUM: FALSE TRUE | |
272 ENUM: NO YES | |
273 BITMASK: ONE TWO FOUR EIGHT SIX_TEEN | |
274 ); | |
275 | |
276 In this case, B<BITS_FOO, BITS_BAR, BITS_CAT, and BITS_DOG> equal 1, 2, 4 and | |
277 8 respectively. B<FALSE and TRUE> equal 0 and 1. B<NO and YES> also equal | |
278 0 and 1. And B<ONE, TWO, FOUR, EIGHT, and SIX_TEEN> equal, you guessed it, 1, | |
279 2, 4, 8, and 16. | |
280 | |
281 =head1 BUGS | |
282 | |
283 Enum names can not be the same as method, function, or constant names. This | |
284 is probably a Good Thing[tm]. | |
285 | |
286 No way (that I know of) to cause compile time errors when one of these enum names get | |
287 redefined. IMHO, there is absolutely no time when redefining a sub is a Good Thing[tm], | |
288 and should be taken out of the language, or at least have a pragma that can cause it | |
289 to be a compile time error. | |
290 | |
291 Enumerated types are package scoped just like constants, not block scoped as some | |
292 other pragma modules are. | |
293 | |
294 It supports A..Z nonsense. Can anyone give me a Real World[tm] reason why anyone would | |
295 ever use this feature...? | |
296 | |
297 =head1 HISTORY | |
298 | |
299 $Log: enum.pm,v $ | |
300 Revision 1.16 1999/05/27 16:00:35 byron | |
301 | |
302 | |
303 Fixed bug that caused bitwise operators to treat enum types as strings | |
304 instead of numbers. | |
305 | |
306 Revision 1.15 1999/05/27 15:51:27 byron | |
307 | |
308 | |
309 Add support for negative values. | |
310 | |
311 Added stricter hex value checks. | |
312 | |
313 Revision 1.14 1999/05/13 15:58:18 byron | |
314 | |
315 | |
316 Fixed bug in hex index code that broke on 0xA. | |
317 | |
318 Revision 1.13 1999/05/13 10:52:30 byron | |
319 | |
320 | |
321 Fixed auto-index bugs in new non-decimal numeric support. | |
322 | |
323 Revision 1.12 1999/05/13 10:00:45 byron | |
324 | |
325 | |
326 Added support for non-decimal numeric representations ala 0x123, 0644, and | |
327 123_456. | |
328 | |
329 First version committed to CVS. | |
330 | |
331 | |
332 Revision 1.11 1998/07/18 17:53:05 byron | |
333 -Added BITMASK and ENUM directives. | |
334 -Revamped documentation. | |
335 | |
336 Revision 1.10 1998/06/12 20:12:50 byron | |
337 -Removed test code | |
338 -Released to CPAN | |
339 | |
340 Revision 1.9 1998/06/12 00:21:00 byron | |
341 -Fixed -w warning when a null tag is used | |
342 | |
343 Revision 1.8 1998/06/11 23:04:53 byron | |
344 -Fixed documentation bugs | |
345 -Moved A..Z case to last as it's not going to be used | |
346 as much as the other cases. | |
347 | |
348 Revision 1.7 1998/06/10 12:25:04 byron | |
349 -Changed interface to match original design by Tom Phoenix | |
350 as implemented in an early version of enum.pm by Benjamin Holzman. | |
351 -Changed tag syntax to not require the 'PREFIX' string of Tom's | |
352 interface. | |
353 -Allow multiple prefix tags to be used at any point. | |
354 -Allowed index value changes from tags. | |
355 | |
356 Revision 1.6 1998/06/10 03:37:57 byron | |
357 -Fixed superfulous -w warning | |
358 | |
359 Revision 1.4 1998/06/10 01:07:03 byron | |
360 -Changed behaver to closer resemble C enum types | |
361 -Changed docs to match new behaver | |
362 | |
363 =head1 AUTHOR | |
364 | |
365 Zenin <zenin@archive.rhps.org> | |
366 | |
367 aka Byron Brummer <byron@omix.com>. | |
368 | |
369 Based off of the B<constant> module by Tom Phoenix. | |
370 | |
371 Original implementation of an interface of Tom Phoenix's | |
372 design by Benjamin Holzman, for which we borrow the basic | |
373 parse algorithm layout. | |
374 | |
375 =head1 COPYRIGHT | |
376 | |
377 Copyright 1998 (c) Byron Brummer. | |
378 Copyright 1998 (c) OMIX, Inc. | |
379 | |
380 Permission to use, modify, and redistribute this module granted under | |
381 the same terms as B<Perl>. | |
382 | |
383 =head1 SEE ALSO | |
384 | |
385 constant(3), perl(1). | |
386 | |
387 =cut |