Expand/Shrink

Enum

An enum declaration can be used to define automatically-incrementing constants.
The default behaviour is for the first item to be 1 and each subsequent item incremented by 1.
An explict value may be specified for any item. Enums may currrently only take integer values (see below).
Such values must always be numeric literals, as opposed to expressions or other variables or constants.
Subsequent values default to the previous value plus one, unless they too are assigned a default value.
enum ONE=1, TWO, THREE, ABC=10, DEF, XYZ

-- ONE is 1, TWO is 2, THREE is 3
-- ABC is 10, DEF is 11, XYZ is 12
Note that C enums start at 0 by default, and you should remember to specify "=0" on the first element when (manually) translating C to Phix, unless of course the enum is just being used to guarantee uniqueness and as such the actual values do not really matter.
It is not a bad idea to always explicitly specify the starting value of an enum, even if it is 1, to avoid confusing anyone more accustomed to the way enums behave in some other programming language.

Of course, on the scale of intuitive comprehension,
enum X, Y
    .. pt[X], pt[Y], ..
simply beats the pants off
    .. pt[1], pt[2], ..
As with other declarations, enums can be terminated with a $ for ease of editing enum lists that change frequently during development.
enum
    YES,
    NO,
--  MAYBE,
--  EITHER,
--  SOMETIMES,
    OFTEN,
--  RARELY,
    $
You can also use $ to replicate the last value, eg
enum ONE,TWO,THREE,ALL=$
?{ONE,TWO,THREE,ALL}            -- {1,2,3,3}
enum UN,UNO=$,DUO,TRE,TRES=$
?{UN,UNO,DUO,TRE,TRES}          -- {1,1,2,3,3}
Attempts to use $ on the very first enum trigger a compilation error ("no prior value").

Sequences use integer indexes, but with enum you may write code like this:
enum X, Y
sequence point = { 0,0 }
point[X] = 3
point[Y] = 4
There is also a special form of enum, an enum type (not supported by pwa/p2js), which is a simple way to write a user defined type based on the set of values in a specific enum group.
The type created this way can be used anywhere a normal user defined or builtin type can be used.
enum type day MON, TUE, WED, THU, FRI, SAT, SUN end type

-- Only allow values of MON..SUN as parameters
function day_difference(day x, day y)
    return abs(y-x)
end function
The above enum statement is in fact (apart from the parameter being named "i") equivalent to:
enum MON, TUE, WED, THU, FRI, SAT, SUN
type day(integer i) return find(i,{MON, TUE, WED, THU, FRI, SAT, SUN}) end type
You may have spotted that there is one significant difference concerning enum types.
Normal type functions return either 1 or 0, whereas enum type functions return a positive integer index when the argument is a member, or 0 if not.
The value returned is the ordinal number of the member in the enum definition, regardless of what the member value is.
Should two enums share the same value, they will share the same ordinal number.
The ordinal numbers of enums surrounding these continue to increment as if every enum had a unique ordinal number, causing some numbers to be skipped.
Lastly, although only applicable for enum type routines that are likely to be invoked more than 106 times, it would be remiss of me not to mention that the use of find in enum types may be slower than say i>=MON and i<=SUN, especially for enum types with several dozen or a few hundred or maybe even thousands of elements, however it is no big deal to replace such with a faster explicit type much as above, should that be a significant concern.
enum type dirn NORTH=4, SOUTH=7, EAST=1, WEST=3 end type

?{NORTH,dirn(NORTH)} -- {4,1}
?{SOUTH,dirn(SOUTH)} -- {7,2}
?{EAST,dirn(EAST)}   -- {1,3}
?{WEST,dirn(WEST)}   -- {3,4}

constant directions = "NSEW",
         directiont = "E?WN??S"

printf(1,"%c\n",directions[dirn(EAST)]) -- E
printf(1,"%c\n",directiont[EAST])       -- E
However if NORTH and EAST were (for some unknown reason) given the same value:
enum type dirn NORTH=4, SOUTH=7, EAST=4, WEST=3 end type
?{NORTH,dirn(NORTH)} -- {4,1}
?{SOUTH,dirn(SOUTH)} -- {7,2}
?{EAST,dirn(EAST)}   -- {4,1}
?{WEST,dirn(WEST)}   -- {3,4}
Note that none of the enums have an ordinal number with a value of 3. This is simply skipped.

Unless an enum member is being set to a specific value, it defaults to 1 more than the previous member.
This default increment can be overridden. The full syntax is:
enum [type name] [by [OP] DELTA] member1, member2, ... ,memberN [end type]
where 'DELTA' is a literal number with an optional +, -, or * operator ('OP') preceding it.
Also, for the same reasons that floating point for loops are prohibited, Phix only supports integer enums, and hence only integer deltas, and is unlikely to ever support "by /N".
enum by 2 A,B,C=6,D      -- values are 1,3,6,8
enum by * 2 A,B,C,D,E    -- values are 1,2,4,8,16
Should both be present, a "by amount" clause must follow rather than precede "type name".
Fairly obviously, the 'end type' is mandatory if there was an earlier 'type', and an error if there wasn’t.

It is also a sensible working practice to add comments such as this:
enum SINGLE_WORD_COMMAND, DIRECTION, NEEDS_DIRECTION, ITEM, NEEDS_ITEM, ALIAS
-- (nb: NEEDS_DIRECTION==DIRECTION+1, NEEDS_ITEM==ITEM+1)

enum N,S,E,W,U,D,INVENT,ATTACK,TAKE,DROP,ALIBI,EQUIP,SLEDGE,LADDER,GOLD,ALL
-- (nb: N/S/E/W/U/D must be 1..6, D must be 6)
-- (nb: SLEDGE/LADDER/GOLD together in that order)
The "(nb:" were added as I was coding and taking advantage of implicit ordering; should I revisit that code six months or six years from now, then I (and other users) should have some clue of things to avoid.