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 |
