Supported RPG Features

DIM(n) on a scalar subfield declares the classic in-DS array table: the subfield's n elements are contiguous bytes of the data structure rather than a separate array object. Element types character, packed, zoned, integer, unsigned and binary are supported; indexed element access and SORTA both work.

DCL-DS Totals;
  Amt PACKED(7:2) DIM(4);
END-DS;

Amt(1) = 20.00;
Amt(2) = -7.00;
SORTA Amt;               // Amt(1) is now -7.00

Because the elements are bytes of the structure, sorting them reorders those bytes in place - so an OVERLAY view of the same storage stays consistent with the sorted order.

SORTA collates by the element's own type: character elements order by their code page, while every numeric element type orders by numeric value. Those differ - as text, 100.00 sorts below 20.00, and a negative sorts below everything - so a numeric table sorts -7.00, 5.00, 20.00, 100.00. An UNS(20) table compares its elements as unsigned, so a value at or above 2^63 sorts last rather than first.

In a QUALIFIED structure the table is reached by its dotted path - the index follows the subfield name, not the structure name:

DCL-DS Q QUALIFIED;
  Code CHAR(3) DIM(3);
END-DS;

Q.Code(1) = 'CCC';
Q.Code(2) = 'AAA';
SORTA Q.Code;            // Q.Code(1) is now 'AAA'

The index may be a variable as well as a literal, and the qualified form supports every element type the unqualified form does. (Do not confuse Q.Code(i) with Q(i).Code - the latter indexes a DIM on the structure, selecting one element of a data-structure array.)

A subfield whose element type has no fixed byte image inside a data structure - an OBJECT reference - has no in-DS array representation and is rejected with TRN1058 rather than laid out at a guessed width.

LIKEDS may also appear on a subfield, making that subfield itself a data structure (a nested data structure). The nesting may continue to any depth, and each leaf is accessed by its full dotted path:

DCL-DS AddrT QUALIFIED TEMPLATE;
  City  CHAR(20);
  State CHAR(2);
END-DS;

DCL-DS Person QUALIFIED;
  Name CHAR(20);
  Addr LIKEDS(AddrT);     // subfield that is itself a data structure
END-DS;

Person.Name      = 'John Smith';
Person.Addr.City = 'New York';   // leaf reached by full path

A LIKEDS subfield has no separate data type - its layout comes entirely from the named template - and the enclosing structure is treated as qualified.

LIKEDS may also be used on a procedure parameter, which receives a data structure laid out like the template. Inside the procedure, the parameter's subfields are read with dot notation:

DCL-PR ShowOrder;
  ord LIKEDS(OrderTemplate) CONST;
END-PR;

DCL-PROC ShowOrder;
  DCL-PI *N;
    ord LIKEDS(OrderTemplate) CONST;
  END-PI;
  DSPLY ('qty=' + %CHAR(ord.Qty));
END-PROC;

A LIKEDS parameter may also be an array of data structures - add DIM(n) on both the prototype and the interface. It is passed by reference (unless declared VALUE/CONST), so a subfield the procedure writes through param(i).subfield is visible in the caller's array after the call:

DCL-PR Bump;
  lines LIKEDS(LineTemplate) DIM(10);
END-PR;

DCL-PROC Bump;
  DCL-PI *N;
    lines LIKEDS(LineTemplate) DIM(10);
  END-PI;
  lines(1).Qty = 999;   // by reference - the caller sees this write
END-PROC;

A character array parameter (CHAR(n) DIM(m)) behaves the same way, and does so across source formats - a fixed-format caller may pass its nA DIM(m) array to a free-format char(n) dim(m) parameter and vice versa. The call links, and an element the procedure writes is visible in the caller's array afterwards:

     DBumpA            PR                  EXTPROC('BUMPA')
     D p                              3A   DIM(2)
     Darr              S              3A   DIM(2)
     C                   CALLP     BumpA(arr)     * arr(1) reflects the write

A procedure may also return a data structure by declaring its interface with LIKEDS. The returned structure can be assigned to a compatible LIKEDS data structure at the call site, copying every subfield:

DCL-PR ApplyPayment LIKEDS(applyResp);
  req LIKEDS(applyReq) CONST;
END-PR;

DCL-PROC ApplyPayment;
  DCL-PI *N LIKEDS(applyResp);
    req LIKEDS(applyReq) CONST;
  END-PI;
  DCL-DS resp LIKEDS(applyResp);
  resp.Message = 'Payment applied.';
  RETURN resp;
END-PROC;

// Caller:
DCL-DS resp LIKEDS(applyResp);
resp = ApplyPayment(req);
DSPLY resp.Message;                  // Payment applied.

A procedure can also return an array, by carrying DIM on the prototype and the interface. The caller receives it as an array and assigns it whole - that is the only way RPG expresses it, since indexing a call result directly is not valid syntax:

DCL-PR Build CHAR(5) DIM(3);
END-PR;

DCL-PROC Build;
  DCL-PI *N CHAR(5) DIM(3);
  END-PI;
  DCL-S r CHAR(5) DIM(3);
  r(1) = 'R1';
  r(2) = 'R2';
  r(3) = 'R3';
  RETURN r;
END-PROC;

// Caller:
DCL-S Got CHAR(5) DIM(3);
Got = Build();
DSPLY Got(2);                        // R2

The same shape in fixed-format - DIM on the PR and on the PI, and the caller assigning the whole array:

     DBuild            PR             5A   DIM(3)
     DGot              S              5A   DIM(3)
     C                   EVAL      Got = Build()
     C     Got(2)        DSPLY
     C                   SETON                                        LR
     PBuild            B
     DBuild            PI             5A   DIM(3)
     Dr                S              5A   DIM(3)
     C                   EVAL      r(2) = 'R2'
     C                   RETURN    r
     PBuild            E

The element type may be character or numeric; a DIM return of either kind hands back the whole array.