MODULE 6 · TABLES · 3/8

Hard-coded tables, INDEXED BY and SET

16 min30 XPExercise

Some tables never change: month names, days per month, state codes. Loading them from input would be overkill, so they are written into the program.

Hard-coding a table with REDEFINES

Traditionally you cannot give each element of an OCCURS table its own VALUE. The classic workaround, found in almost every legacy program, is to lay the values out as ordinary FILLER fields, then redefine the same bytes as a table:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. IDXDEMO.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DAY-VALUES.
           05  FILLER         PIC X(3) VALUE "MON".
           05  FILLER         PIC X(3) VALUE "TUE".
           05  FILLER         PIC X(3) VALUE "WED".
           05  FILLER         PIC X(3) VALUE "THU".
           05  FILLER         PIC X(3) VALUE "FRI".
       01  WS-DAY-TABLE REDEFINES WS-DAY-VALUES.
           05  WS-DAY-NAME    PIC X(3) OCCURS 5 TIMES
                              INDEXED BY DAY-IX.
       01  WS-POS             PIC 9(2).
       PROCEDURE DIVISION.
           SET DAY-IX TO 1
           DISPLAY "FIRST: " WS-DAY-NAME(DAY-IX)
           SET DAY-IX UP BY 2
           DISPLAY "THIRD: " WS-DAY-NAME(DAY-IX)
           SET DAY-IX DOWN BY 1
           SET WS-POS TO DAY-IX
           DISPLAY "POSITION " WS-POS ": " WS-DAY-NAME(DAY-IX)
           STOP RUN.

Output:

FIRST: MON
THIRD: WED
POSITION 02: TUE

REDEFINES means "these are the same bytes, described differently". The 15 bytes MONTUEWEDTHUFRI are both five FILLERs and a five-element table. The two layouts must be the same length.

Values can be packed several to a FILLER, and elements can be groups:

       01  WS-MONTH-VALUES.
           05  FILLER         PIC X(15) VALUE "JAN31FEB28MAR31".
       01  WS-MONTH-TABLE REDEFINES WS-MONTH-VALUES.
           05  WS-MONTH       OCCURS 3 TIMES INDEXED BY MON-IX.
               10  WS-MON-NAME    PIC X(3).
               10  WS-MON-DAYS    PIC 9(2).

Here WS-MON-DAYS(2) is 28: digits in a text literal are valid display-numeric data once redefined as PIC 9.

Indexes: INDEXED BY

INDEXED BY DAY-IX declares an index that belongs to the table. You do not define DAY-IX in WORKING-STORAGE. The clause creates it. Use it wherever a subscript would go.

Why a separate kind of pointer? A subscript is an occurrence number (1, 2, 3…), and every reference has to multiply it by the element length. On IBM mainframes an index holds the byte offset itself, so no multiplication is needed. It was a significant speed-up on 1970s hardware, and SEARCH (next lesson) requires an index.

SET: the only way to change an index

Statement Effect
SET DAY-IX TO 1 Point at occurrence 1
SET DAY-IX TO WS-START Point at the occurrence in a numeric field
SET DAY-IX UP BY 2 Move forward two occurrences
SET DAY-IX DOWN BY 1 Move back one
SET WS-POS TO DAY-IX Copy the occurrence number into a numeric field

PERFORM VARYING DAY-IX FROM 1 BY 1 UNTIL DAY-IX > 5 also works, and comparing an index with a number (IF MON-IX = 12) compares occurrence numbers.

Not MOVE, not ADD

An index is not an ordinary number. IBM Enterprise COBOL rejects MOVE, ADD or DISPLAY on an index name, so use SET, and SET a numeric field from the index when you need to display it. GnuCOBOL is more lenient, but code written that way will not compile on the mainframe.

On the job

Shops name indexes with an -IX or -IDX suffix and subscripts with -SUB, so a reader knows at a glance which is which. Mixing them up (using an index from one table on another table with a different element length) is a classic bug, because the offset is wrong for the other table.

Your task

A finance company needs the months of a repayment plan. Read two lines:

  1. The month of the first payment, 1 to 12
  2. The number of payments, e.g. 4

Display one line per payment with the month name and its number of days (ignore leap years):

PAYMENT 01: NOV (30 DAYS)
PAYMENT 02: DEC (31 DAYS)
PAYMENT 03: JAN (31 DAYS)
PAYMENT 04: FEB (28 DAYS)

After December the plan wraps round to January. If the first month is not 1–12, display only INVALID MONTH.

The starter has the first three months of the table. Complete the hard-coded table (all 12 months, REDEFINES with VALUE), give it an index with INDEXED BY MON-IX, and move through it with SET.

Days per month: JAN 31, FEB 28, MAR 31, APR 30, MAY 31, JUN 30, JUL 31, AUG 31, SEP 30, OCT 31, NOV 30, DEC 31.

fixed format
Run your program to see its output here. The first visible test's input and datasets are used.
Submit to grade your program against every test.