Mercurial > repos > jjohnson > crest
diff enum.pm @ 0:acc8d8bfeb9a
Uploaded
author | jjohnson |
---|---|
date | Wed, 08 Feb 2012 16:59:24 -0500 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enum.pm Wed Feb 08 16:59:24 2012 -0500 @@ -0,0 +1,387 @@ +package enum; +use strict; +no strict 'refs'; # Let's just make this very clear right off + +use Carp; +use vars qw($VERSION); +$VERSION = do { my @r = (q$Revision: 1.16 $ =~ /\d+/g); sprintf '%d.%03d'.'%02d' x ($#r-1), @r}; + +my $Ident = '[^\W_0-9]\w*'; + +sub ENUM () { 1 } +sub BITMASK () { 2 } + +sub import { + my $class = shift; + @_ or return; # Ignore 'use enum;' + my $pkg = caller() . '::'; + my $prefix = ''; # default no prefix + my $index = 0; # default start index + my $mode = ENUM; # default to enum + + ## Pragmas should be as fast as they can be, so we inline some + ## pieces. + foreach (@_) { + ## Plain tag is most common case + if (/^$Ident$/o) { + my $n = $index; + + if ($mode == ENUM) { + $index++; + } + elsif ($mode == BITMASK) { + $index ||= 1; + $index *= 2; + if ( $index & ($index - 1) ) { + croak ( + "$index is not a valid single bitmask " + . " (Maybe you overflowed your system's max int value?)" + ); + } + } + else { + confess qq(Can't Happen: mode $mode invalid); + } + + *{"$pkg$prefix$_"} = sub () { $n }; + } + + ## Index change + elsif (/^($Ident)=(-?)(.+)$/o) { + my $name= $1; + my $neg = $2; + $index = $3; + + ## Convert non-decimal numerics to decimal + if ($index =~ /^0x[\da-f]+$/i) { ## Hex + $index = hex $index; + } + elsif ($index =~ /^0\d/) { ## Octal + $index = oct $index; + } + elsif ($index !~ /[^\d_]/) { ## 123_456 notation + $index =~ s/_//g; + } + + ## Force numeric context, but only in numeric context + if ($index =~ /\D/) { + $index = "$neg$index"; + } + else { + $index = "$neg$index"; + $index += 0; + } + + my $n = $index; + + if ($mode == BITMASK) { + ($index & ($index - 1)) + and croak "$index is not a valid single bitmask"; + $index *= 2; + } + elsif ($mode == ENUM) { + $index++; + } + else { + confess qq(Can't Happen: mode $mode invalid); + } + + *{"$pkg$prefix$name"} = sub () { $n }; + } + + ## Prefix/option change + elsif (/^([A-Z]*):($Ident)?(=?)(-?)(.*)/) { + ## Option change + if ($1) { + if ($1 eq 'ENUM') { $mode = ENUM; $index = 0 } + elsif ($1 eq 'BITMASK') { $mode = BITMASK; $index = 1 } + else { croak qq(Invalid enum option '$1') } + } + + my $neg = $4; + + ## Index change too? + if ($3) { + if (length $5) { + $index = $5; + + ## Convert non-decimal numerics to decimal + if ($index =~ /^0x[\da-f]+$/i) { ## Hex + $index = hex $index; + } + elsif ($index =~ /^0\d/) { ## Oct + $index = oct $index; + } + elsif ($index !~ /[^\d_]/) { ## 123_456 notation + $index =~ s/_//g; + } + + ## Force numeric context, but only in numeric context + if ($index =~ /\D/) { + $index = "$neg$index"; + } + else { + $index = "$neg$index"; + $index += 0; + } + + ## Bitmask mode must check index changes + if ($mode == BITMASK) { + ($index & ($index - 1)) + and croak "$index is not a valid single bitmask"; + } + } + else { + croak qq(No index value defined after "="); + } + } + + ## Incase it's a null prefix + $prefix = defined $2 ? $2 : ''; + } + + ## A..Z case magic lists + elsif (/^($Ident)\.\.($Ident)$/o) { + ## Almost never used, so check last + foreach my $name ("$1" .. "$2") { + my $n = $index; + + if ($mode == BITMASK) { + ($index & ($index - 1)) + and croak "$index is not a valid single bitmask"; + $index *= 2; + } + elsif ($mode == ENUM) { + $index++; + } + else { + confess qq(Can't Happen: mode $mode invalid); + } + + *{"$pkg$prefix$name"} = sub () { $n }; + } + } + + else { + croak qq(Can't define "$_" as enum type (name contains invalid characters)); + } + } +} + +1; + +__END__ + + +=head1 NAME + +enum - C style enumerated types and bitmask flags in Perl + +=head1 SYNOPSIS + + use enum qw(Sun Mon Tue Wed Thu Fri Sat); + # Sun == 0, Mon == 1, etc + + use enum qw(Forty=40 FortyOne Five=5 Six Seven); + # Yes, you can change the start indexs at any time as in C + + use enum qw(:Prefix_ One Two Three); + ## Creates Prefix_One, Prefix_Two, Prefix_Three + + use enum qw(:Letters_ A..Z); + ## Creates Letters_A, Letters_B, Letters_C, ... + + use enum qw( + :Months_=0 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + :Days_=0 Sun Mon Tue Wed Thu Fri Sat + :Letters_=20 A..Z + ); + ## Prefixes can be changed mid list and can have index changes too + + use enum qw(BITMASK:LOCK_ SH EX NB UN); + ## Creates bitmask constants for LOCK_SH == 1, LOCK_EX == 2, + ## LOCK_NB == 4, and LOCK_UN == 8. + ## NOTE: This example is only valid on FreeBSD-2.2.5 however, so don't + ## actually do this. Import from Fnctl instead. + +=head1 DESCRIPTION + +Defines a set of symbolic constants with ordered numeric values ala B<C> B<enum> types. + +Now capable of creating creating ordered bitmask constants as well. See the B<BITMASKS> +section for details. + +What are they good for? Typical uses would be for giving mnemonic names to indexes of +arrays. Such arrays might be a list of months, days, or a return value index from +a function such as localtime(): + + use enum qw( + :Months_=0 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + :Days_=0 Sun Mon Tue Wed Thu Fri Sat + :LC_=0 Sec Min Hour MDay Mon Year WDay YDay Isdst + ); + + if ((localtime)[LC_Mon] == Months_Jan) { + print "It's January!\n"; + } + if ((localtime)[LC_WDay] == Days_Fri) { + print "It's Friday!\n"; + } + +This not only reads easier, but can also be typo-checked at compile time when +run under B<use strict>. That is, if you misspell B<Days_Fri> as B<Days_Fry>, +you'll generate a compile error. + +=head1 BITMASKS, bitwise operations, and bitmask option values + +The B<BITMASK> option allows the easy creation of bitmask constants such as +functions like flock() and sysopen() use. These are also very useful for your +own code as they allow you to efficiently store many true/false options within +a single integer. + + use enum qw(BITMASK: MY_ FOO BAR CAT DOG); + + my $foo = 0; + $foo |= MY_FOO; + $foo |= MY_DOG; + + if ($foo & MY_DOG) { + print "foo has the MY_DOG option set\n"; + } + if ($foo & (MY_BAR | MY_DOG)) { + print "foo has either the MY_BAR or MY_DOG option set\n" + } + + $foo ^= MY_DOG; ## Turn MY_DOG option off (set its bit to false) + +When using bitmasks, remember that you must use the bitwise operators, B<|>, B<&>, B<^>, +and B<~>. If you try to do an operation like C<$foo += MY_DOG;> and the B<MY_DOG> bit +has already been set, you'll end up setting other bits you probably didn't want to set. +You'll find the documentation for these operators in the B<perlop> manpage. + +You can set a starting index for bitmasks just as you can for normal B<enum> values, +but if the given index isn't a power of 2 it won't resolve to a single bit and therefor +will generate a compile error. Because of this, whenever you set the B<BITFIELD:> +directive, the index is automatically set to 1. If you wish to go back to normal B<enum> +mode, use the B<ENUM:> directive. Similarly to the B<BITFIELD> directive, the B<ENUM:> +directive resets the index to 0. Here's an example: + + use enum qw( + BITMASK:BITS_ FOO BAR CAT DOG + ENUM: FALSE TRUE + ENUM: NO YES + BITMASK: ONE TWO FOUR EIGHT SIX_TEEN + ); + +In this case, B<BITS_FOO, BITS_BAR, BITS_CAT, and BITS_DOG> equal 1, 2, 4 and +8 respectively. B<FALSE and TRUE> equal 0 and 1. B<NO and YES> also equal +0 and 1. And B<ONE, TWO, FOUR, EIGHT, and SIX_TEEN> equal, you guessed it, 1, +2, 4, 8, and 16. + +=head1 BUGS + +Enum names can not be the same as method, function, or constant names. This +is probably a Good Thing[tm]. + +No way (that I know of) to cause compile time errors when one of these enum names get +redefined. IMHO, there is absolutely no time when redefining a sub is a Good Thing[tm], +and should be taken out of the language, or at least have a pragma that can cause it +to be a compile time error. + +Enumerated types are package scoped just like constants, not block scoped as some +other pragma modules are. + +It supports A..Z nonsense. Can anyone give me a Real World[tm] reason why anyone would +ever use this feature...? + +=head1 HISTORY + + $Log: enum.pm,v $ + Revision 1.16 1999/05/27 16:00:35 byron + + + Fixed bug that caused bitwise operators to treat enum types as strings + instead of numbers. + + Revision 1.15 1999/05/27 15:51:27 byron + + + Add support for negative values. + + Added stricter hex value checks. + + Revision 1.14 1999/05/13 15:58:18 byron + + + Fixed bug in hex index code that broke on 0xA. + + Revision 1.13 1999/05/13 10:52:30 byron + + + Fixed auto-index bugs in new non-decimal numeric support. + + Revision 1.12 1999/05/13 10:00:45 byron + + + Added support for non-decimal numeric representations ala 0x123, 0644, and + 123_456. + + First version committed to CVS. + + + Revision 1.11 1998/07/18 17:53:05 byron + -Added BITMASK and ENUM directives. + -Revamped documentation. + + Revision 1.10 1998/06/12 20:12:50 byron + -Removed test code + -Released to CPAN + + Revision 1.9 1998/06/12 00:21:00 byron + -Fixed -w warning when a null tag is used + + Revision 1.8 1998/06/11 23:04:53 byron + -Fixed documentation bugs + -Moved A..Z case to last as it's not going to be used + as much as the other cases. + + Revision 1.7 1998/06/10 12:25:04 byron + -Changed interface to match original design by Tom Phoenix + as implemented in an early version of enum.pm by Benjamin Holzman. + -Changed tag syntax to not require the 'PREFIX' string of Tom's + interface. + -Allow multiple prefix tags to be used at any point. + -Allowed index value changes from tags. + + Revision 1.6 1998/06/10 03:37:57 byron + -Fixed superfulous -w warning + + Revision 1.4 1998/06/10 01:07:03 byron + -Changed behaver to closer resemble C enum types + -Changed docs to match new behaver + +=head1 AUTHOR + +Zenin <zenin@archive.rhps.org> + +aka Byron Brummer <byron@omix.com>. + +Based off of the B<constant> module by Tom Phoenix. + +Original implementation of an interface of Tom Phoenix's +design by Benjamin Holzman, for which we borrow the basic +parse algorithm layout. + +=head1 COPYRIGHT + +Copyright 1998 (c) Byron Brummer. +Copyright 1998 (c) OMIX, Inc. + +Permission to use, modify, and redistribute this module granted under +the same terms as B<Perl>. + +=head1 SEE ALSO + +constant(3), perl(1). + +=cut