# Pascal Programming/Enumerations

One powerful notational as well as syntactical tool of Pascal is the declaration of custom enumeration data types.

## Handling

### Notion

An enumeration data type is a finite list of named discrete values. Enumerations virtually give names to individual integer values, however, you cannot (directly) do arithmetic operations on it.

### Declaration

An enumeration data type is declared by following the data type identifier with a non-empty comma-separated list of (new, not previously used) identifiers.

type
weekday = (Monday, Tuesday, Wednesday, Thursday, Friday,
Saturday, Sunday);


The individual list items refer to specific values the data type may assume. The data type identifier identifies the data type as a whole.

### Operations

Once an enumeration data type has been declared, you can use it like any other data type:

var
startOfWeek: weekday;
begin
startOfWeek := Sunday;
end.


The variable startOfWeek is restricted to assume only legal values of the data type weekday. Note that Sunday is not enclosed by typewriter quotation marks (') which usually indicate a string literal. The identifier Sunday indicates a value in its own right.

### Ordinal values

#### Automatism

Every enumeration data type declaration implicitly defines an order. The comma-separated list is per definition a sorted list. The built‑in function ord, short for ordinal value, gives you the opportunity to obtain the ordinal value of an enumeration element, that is an integer-value unique/specific to that enumeration member.

The first element of an enumeration is numbered as 0. The second, if applicable, has the number 1, and so forth.

#### Override

Some compilers, such as the FPC, allow you to specify explicit indexes for some, or even all elements of an enumeration:

type
month = (January := 1, February, March, April, May, June,
July, August, September, October, November, December);


Here, January will have the ordinal value 1. And all following items have an ordinal value greater than 1. The automatic assignment of numbers still ensures every enumeration member has a unique number among the entire enumeration data type. February will have the ordinal value 2, March the value 3, and so on. The value 0, however, is not assigned to any element of that enumeration.

 If you specify explicit indices for particular elements, you need to ensure all numbers are ascending order. You cannot assign the same number twice. If you skip giving numbers, the automatic numbering system will assign and “reserve” numbers. You cannot use numbers the automatism uses.

Specifying explicit indices is a non-standard extension. In FPC’s {$mode Delphi} you need to use a plain equal sign (=) instead of :=. This is also referred to as “C‑style enumeration declaration”, since the programming language C uses that syntax. #### Inverse Pascal does not provide a generic function that lets you determine the enumeration element based on a number. There is no function returning January, for instance, if it is supplied with the integer-value 1.[fn 1] ### Neighbors The standard functions pred and succ, short for predecessor and successor respectively, are automatically defined for every enumeration data type. These functions return the previous or next value of an enumeration value. For example succ(January) will return February, as it is the successor of the value January. However, pred(January) will fail as there is technically no member prior January. An enumeration list is not cyclical. Although in real life January follows December, the enumeration data type month does not “know” that. The EP standard allows a second optional integer parameter to be supplied to either pred or succ. succ(January, 2) is identical to succ(succ(January)), yet more convenient and shorter, but also pred(January, -2) returns the same value. Utilizing this functionality you can obtain an enumeration value given its index. succ(Monday, 3) evaluates to the weekday value that has the ordinal value 3, thus virtually providing a means for an inverse ord function. However, it is necessary to know the first element of the enumeration though, and the enumeration may not use any explicit indices in its declaration (unless all indices coincide with the automatic numbering pattern). ### Operators Enumeration data type values are automatically eligible to be used with several operators. Since every enumeration value has an ordinal value, they can be ordered and you can test for that. The relational operators • < • > • <= • >= • = • <> work in conjunction with enumeration values. For example, January < February will evaluate to true, because January has a smaller ordinal value than February. Although, technically you can compare apples and oranges (spoiler alert: they are unequal), all relational operators only work in conjunction with two values of the same kind. In Pascal, you cannot compare a weekday value with a month value. Nonetheless, something like ord(August) > ord(Monday) is legal, since you are then in fact comparing integer values. Note, arithmetic operators (+, -, and so on) do not work with enumeration data types, despite their ordinal values. ## Boolean as an enumeration data type ### Definition The data type Boolean is a built‑in special enumeration data type. It is guaranteed that • ord(false) = 0, • ord(true) = 1, and, in consequence, • pred(true) = false. ### Logical operators Boolean is only enumeration data type operations can be directly performed on using logical operators. #### Negation The most basic operator is the negation. It is a unary operator, that means it expects only one operand. In Pascal it uses the keyword not. By preceding a Boolean expression with not (and some separator such as a space character), the expression is negated. expression result not true false not false true not #### Conjunction While this may be pretty straightforward, the so-called logical conjunction, indicated by and, might not be. The truth table for it looks like this: value of tired value of intoxicated result of tired and intoxicated false false false false true false true false false true true true conjunction truth table In EE this is frequently written as ${\displaystyle \cdot }$ (“times”) or even omitted, because (like an mathematics) an invisible “times” is assumed. Given that the ordinal values of false and true are as defined above, you could calculate the and result by multiplying them. #### Disjunction A little more confusing, because it may be contradictory to someone’s natural language, is the word or. If either operand is true, the overall expression’s result becomes true. value of raining value of snowing result of raining or snowing false false false false true true true false true true true true disjunction truth table Electrical engineers frequently use the ${\displaystyle +}$ symbol to denote this operation. With respect to Boolean’s ordinal value, though, you must “define” that ${\displaystyle 1+1}$ was still ${\displaystyle 1}$. #### Precedence Like the usual rule in mathematics “multiplication and division first, then addition and subtraction”, a conjunction is evaluated first before a disjunction is. However, since the negation is a unary operator, it is evaluated first in any case. That means you must be really careful not to forget placing parenthesis. The expression not hungry or thirsty  is fundamentally different to not (hungry or thirsty)  ## Ranges ### Ordinal types Enumeration data types belong to the category of ordinal data types. Other ordinal data types are: • integer, • char, • and all enumeration data types, including Boolean. They all have in common, that a value of them can be mapped to a distinct integer-value. The ord function lets you retrieve that value. ### Intervals Sometimes, it makes sense to restrict a set of values to a certain range. For instance, the hours on a military time clock may show values from 0 up to and including 23. Yet the data type integer will permit other values too. Pascal allows you to declare (sub‑)range data types. A (sub‑)range data type has a host data type, e. g. integer, and two limits. One lower and one upper limit. A range is specified by giving the limits in ascending order, separated through two periods back-to-back (..): type majuscule = 'A'..'Z';  The limits may be given as any computable expression, as long as it does not depend on run-time data.[fn 2] For example constants (that have already been defined) may be used: type integerNonNegative = 0..maxInt;  Note, we named this range integerNonNegative and not nonNegativeInteger, because this will facilitate alphabetical sorting of some documentation tools or in IDEs. ### Restriction A variable possessing one (sub‑)range data type may only assume values within the range. If the variable exceeds its legal range, the program aborts. The following error message may appear (memory address at the end can vary): ./a.out: value out of range (error #300 at 402a54)  The corresponding test program has been compiled with GPC. Other compilers may emit different messages. The default configuration of the FPC, however, ignores this. Assigning out-of-range values to variables will not yield an error (if it depends on run-time data). The developers of the FPC cite compatibility reasons to other compilers, which decided to ignore out-of-range values for speed reasons.[fn 3] You need to specifically request that illegal values cannot be assigned to ordinal type variables. This can be done by placing a specially crafted comment prior any (crucial) assignments: {$rangeChecks on} (case-insensitive) or {\$R+} for short (case-sensitive) will ensure illegal values are not assigned and the program aborts if any attempts are made anyway. Specifying this compiler switch once in your source code file is sufficient. FPC’s ‑Cr command-line switch has the same effect.

 The range restriction has to be met when storing the value, when saving the value to a variable for instance. Intermediate calculations, such as when evaluating an expression, may fall out of range though.

## Selections

With the advent of enumeration data types, it may become cumbersome and tedious to check for values just using if‑branches.

### Explanation

The case selection statement unites multiple exclusive if‑branches in one language construct.[fn 4]

case sign(x) of
-1:
begin
writeLn('You have entered a negative number.');
end;
0:
begin
writeLn('The numbered you have entered is sign-less.');
end;
1:
begin
writeLn('That is a positive number.');
end;
end;


Between case and of any expression that evaluates to an ordinal value may appear. After that, -1:, 0: and 1: are case labels. These case labels mark the start of alternatives. After a case label follows a statement.

-1, 0 and 1 denote case values. Every case label consists of a non-empty comma-separated list of case values, followed by a colon (:). All case values have to be legal constant values, constant expressions, that are compatible to the comparison expression above, what is written between case and of. Every specified case value needs to appear exclusively in one case label. No case label value can appear twice. It is not necessary to put them in order, according their ordinal value, although it can make your source code more readable.

### Shorthand for many cases

In EP case labels may contain ranges.

program letterReport(input, output);
var
c: char;
begin
write('Give me a letter: ');

case c of
'A'..'Z':
begin
writeLn('Wow! That’s a big letter!');
end;
'a'..'z':
begin
writeLn('What a nice small letter.');
end;
end;
end.


This shorthand notation allows you to catch many cases. The case label 'A'..'Z': includes all upper-case letters, without requiring you to list them all individually.

Take care that no range overlaps with other case label values. This is forbidden. Good processors will complain about such a mistake though. The GPC yields the error message duplicate case-constant in case' statement, the FPC reports just duplicate case label[fn 5], both telling you some information about the location in your source code.

### Fall-back

It is important that any (expected) value of the comparison expression matches one case label. If the comparison expression evaluates to a value no case label contains the corresponding value, the program aborts.[fn 6] If this is not desired the “Extended Pascal” standard allows a special case label called otherwise (note, without a colon). This case treats all values that have no explicit case label associated with them.

program asciiTest(input, output);
var
c: char;
begin
write('Supply any character: ');
case c of
// empty statement, so the control characters are not
// considered by the otherwise-branch as non-ASCII characters
#0..#31, #127: ;
#32..#126:
begin
writeLn('You entered an ASCII printable character.');
end;
otherwise
begin
writeLn('You entered a non-ASCII character.');
end;
end;
end.


otherwise may only appear at the end. There must be at least one case label beforehand, otherwise (no pun intended) the otherwise case is always taken, rendering the entire case-statement useless.

BP, that is Delphi, re-uses the word else having the same semantics, the same meaning as otherwise. The FPC and GPC support both, although GPC can be instructed to only accept otherwise.

Write a concise function that returns the successor of month, but for December the value January is returned.
Using knowledge you have learned in this unit, a case statement is just perfect:
function successor(start: month): month;
begin
case start of
January..November:
begin
successor := succ(start);
end;
December:
begin
successor := January;
end;
end;
end;


For the purposes of this exercise (demonstrating that relational operators such as < are automatically defined for enumeration data types) the following is acceptable too:

function successor(start: month): month;
begin
if start < December then
begin
successor := succ(start);
end
else
begin
successor := January;
end;
end;


Yet the case-implementation is, mathematically speaking, more precise. In the first implementation, if the parameter is wrong, out of range, the program aborts.

The latter implementation with if … then … else will be “wrongly” defined for illegal values too.
Using knowledge you have learned in this unit, a case statement is just perfect:
function successor(start: month): month;
begin
case start of
January..November:
begin
successor := succ(start);
end;
December:
begin
successor := January;
end;
end;
end;


For the purposes of this exercise (demonstrating that relational operators such as < are automatically defined for enumeration data types) the following is acceptable too:

function successor(start: month): month;
begin
if start < December then
begin
successor := succ(start);
end
else
begin
successor := January;
end;
end;


Yet the case-implementation is, mathematically speaking, more precise. In the first implementation, if the parameter is wrong, out of range, the program aborts.

The latter implementation with if … then … else will be “wrongly” defined for illegal values too.

Notes:

1. Some compilers, such as the FPC, allow “typecasting” effectively transforming 1 into January. However, this – typecasting – is not a function, especially typecasting does not work properly if the values are out of range (no RTE-generation, nor whatsoever).
2. This is an “Extended Pascal” (ISO 10206) extension. In Standard “unextended” Pascal (ISO 7185) only constants are allowed.
3. The Pascal ISO standards do allow this. It is at the compiler programmer’s discretion to ignore such errors. Nonetheless, accompanying documents (manuals, etc.) are meant to point that out.
4. This is an analogy. case-statements are usually not translated into a series of if‑branches.
5. This error message is imprecise. The error message of GPC is more correct. The problem is that a certain value, “case-constant”, appears multiple times.
6. Many compilers do not respect this requirement in their default configuration. The GPC needs to be instructed to be “completely” ISO-compliant (‑‑classic‑pascal, ‑‑extended‑pascal, or just ‑‑case‑value‑checking`). In BP, Delphi will just continue, leaving a missing case unnoticed. As of version 3.2.0 the FPC does not regard this requirement at all.

Next Page: Sets | Previous Page: Routines
Home: Pascal Programming