Repository navigation
Fixed Format Source
OpenRPG accepts classic column-based RPG IV source as well as free-format. This is a second parsing frontend only — it builds the same AST, so every language feature described elsewhere in this guide is reachable from fixed-format source.
The format is detected from the content of columns 1-6 of the first substantive
line, not from the file extension and not from a **FREE directive. A file can
also mix the two: H/F/D specs in columns with the calculations already
modernized inside /free.../end-free blocks is a common
incremental-modernization pattern and works as-is.
Position 6 selects the spec type. Positions 1-5 are the sequence number and are
ignored; a * in position 7 makes the whole line a comment.
| Position 6 | Spec | Purpose |
|---|---|---|
H |
Control | Compile options — same set as CTL-OPT
|
F |
File Description | File declarations — same as DCL-F
|
D |
Definition | Standalone fields and data structures — same as DCL-S and DCL-DS
|
I |
Input | Program-described input record layout (see next chapter) |
C |
Calculation | Executable statements |
O |
Output | Program-described output record layout (see next chapter) |
There is no P-spec support, so fixed-format source cannot declare a
procedure. Anything needing DCL-PROC — including ON-EXIT — has to live in a
/free block.
Positions 7-80 are a free keyword list, identical to what CTL-OPT accepts:
HDFTACTGRP(*NO) DATFMT(*ISO) TIMFMT(*HMS) FCUSTFL IF E DISK KEYED EXTDESC('customers')
FCUSTMENU CF E WORKSTN
FTESTFL O F 25 DISK| Positions | Field |
|---|---|
| 7-16 | File name |
| 17 | File type (I/O/U/C) |
| 18 | File designation |
| 19 | End-of-file flag |
| 20 | Add flag |
| 21 | Sequence |
| 22 | File format (E externally described, F program described) |
| 23-27 | Record length (program-described files) |
| 28 | Process mode |
| 29-33 | Key field length |
| 34 | Record address type |
| 35 | File organization |
| 36-42 | Device — DISK and WORKSTN are implemented |
| 44-80 | Keyword tail, continuable across lines |
DcustName S 30A
DorderDS DS
D orderNo 7P 0
D custName2 30A VARYING
D qty 5P 0 DIM(12)| Positions | Field |
|---|---|
| 7-21 | Name — continuable with a trailing ...
|
| 22 | External-description flag |
| 23 | Data structure type |
| 24-25 | Definition type (blank, C, DS, PR, PI, S) |
| 26-32 | From position |
| 33-39 | To position or length |
| 40 | Data type |
| 41-42 | Decimal positions |
| 44-80 | Keyword tail, continuable across lines |
Only definition types S (standalone field), DS (data structure) and blank
(a subfield) are accepted. C for a named constant, and PR/PI for
prototypes and procedure interfaces, are rejected — declare those in a /free
block instead.
Supported in the keyword tail: VARYING{(2|4)} (which produces a VARCHAR —
free-format spells this VARCHAR(n) instead), DIM(n) on the data structure
itself for an array of elements, and per-subfield OVERLAY(field),
OVERLAY(field:pos), LIKEDS(name), LIKE(other) and DIM(n). POS is a
free-form keyword and is rejected here (IBM: RNF3555): a fixed-form subfield
gives its From and To positions in 26-32 and 33-39, and its size follows from
them (a 4-byte I subfield holds 10 digits, n packed bytes 2n-1):
Drecord DS QUALIFIED
Did 1 4I 0
Dname 5 24AA name longer than the name positions continues with ... on a line of its
own, and the rest of the definition goes on the line that completes it (IBM:
RNF0622):
DcustomerNumb...
Der S 10I 0A subfield DIM(n) is an array inside the structure, and is subscripted with
the ds.field(index) form:
DorderDS DS
D qty 5P 0 DIM(12)
/free
orderDS.qty(1) = 5;
/end-freeA subfield LIKE(other) resolves against a field declared earlier in the same
data structure. In a QUALIFIED data structure that sibling is named
LIKE(ds.other); alone, other is not defined there (IBM: RNF7030). Combining LIKE and DIM on one subfield is not supported,
and a subfield DIM is fixed-size only — no DIM(*VAR)/DIM(*AUTO).
Calculations come in three shapes, and all three can be mixed freely in one file.
| Positions | Field |
|---|---|
| 7-8 |
SR, or AN/OR for a multi-indicator group |
| 9-11 | Conditioning indicator |
| 12-25 | Factor 1 |
| 26-35 | Operation code, plus any (extender)
|
| 36-49 | Factor 2 (traditional syntax) |
| 36-80 | The whole expression (extended factor 2) |
| 50-63 | Result (traditional syntax) |
| 71-76 | Resulting indicators — blank except on COMP
|
Positions 64-70 (inline field length and decimals) must be blank; declare the field on a D-spec instead.
Extended factor 2 puts a normal free-format expression in positions 36-80, and continues onto further lines that leave 7-35 blank:
C IF custBal > 1000 AND status = 'A'
C EVAL msg = 'Dear ' + %TRIM(firstName) +
C ', your account is current.'
C ENDIFTraditional syntax uses Factor 1, Factor 2 and Result:
C custKey CHAIN CUSTFL
C EXSR PRTLINE
C total DSPLY/free blocks hand their contents to the free-format parser unchanged:
/free
total = qty * price;
DSPLY %CHAR(total);
/end-free| Group | Opcodes |
|---|---|
| Block structure |
IF ELSEIF ELSE ENDIF DOW DOU ENDDO FOR FOR-EACH ENDFOR SELECT WHEN OTHER ENDSL MONITOR ON-ERROR ENDMON ITER LEAVE
|
| Subroutines |
BEGSR ENDSR EXSR LEAVESR
|
| Assignment |
EVAL EVALR EVAL-CORR CLEAR RESET MOVE MOVEL Z-ADD Z-SUB
|
| Arithmetic |
ADD SUB MULT DIV
|
| Comparison / branching |
COMP CASxx CABxx GOTO TAG
|
| Calls |
CALLP CALL PARM PLIST RETURN
|
| File I/O |
CHAIN READ READP READE READPE WRITE UPDATE DELETE SETLL SETGT
|
| Other |
DSPLY SORTA XML-INTO DATA-INTO DATA-GEN SND-MSG
|
Two notes on the file opcodes: none of them takes a data-structure result
operand, and DELETE takes no key — it deletes the last record read.
SETLL and SETGT do not accept an operation extender; the other file opcodes
do.
Position 9 holds a blank or N, and positions 10-11 the indicator number. The
statement runs only when the indicator matches:
C 10 EVAL msg = 'Ten is on'
C N10 EVAL msg = 'Ten is off'Only self-contained statements can be conditioned — assignments, calls,
ITER/LEAVE/LEAVESR, the file opcodes, GOTO, DSPLY, and the arithmetic
set. Conditioning a block-structure opcode is a compile error, because the
wrapper would leave the block unbalanced; fold the test into the block's own
condition instead. TAG cannot be conditioned either.
A C-spec line has room for exactly one indicator, so AN/OR in positions 7-8
combine the indicators of consecutive lines. The operation code sits on the
last line of the group, and RPG relates the terms as an OR of AND-groups:
C 10
CAN 30
COR 20 EVAL msg = '(10 and 30) or 20'Only *IN01-*IN99 are supported. LR, MR, RT, OV, 1P, L1-L9,
H1-H9, U1-U8, KA-KY and OA-OG are recognized by name so they get
a clear "not supported" message rather than being reported as a typo.
GOTO and TAG have no free-format equivalent at all, so they are accepted
only from fixed columns — writing either inside a /free block is an error.
Since subroutines generate as lambdas, a GOTO cannot cross a subroutine
boundary, which is RPG's own rule. Both take their label in Factor 2, and
leave Factor 1 and the Result field blank.
C GOTO SKIP
C EVAL msg = 'not reached'
C TAG SKIPADD/SUB/MULT/DIV use Factor 1 when it is present and accumulate into
the result when it is blank. Z-ADD/Z-SUB never use Factor 1.
| Written | Means |
|---|---|
C a ADD b c |
c = a + b |
C ADD b c |
c = c + b |
C Z-ADD b c |
c = b |
C Z-SUB b c |
c = -b |
Extenders such as (H) half-adjust pass straight through.
MOVE and MOVEL are positional moves, not assignments. MOVE aligns
factor 2 against the right end of the result and MOVEL against the left, and
whatever part of the result the move does not reach is left unchanged — the
(P) extender blanks it instead. An over-long factor 2 truncates on the side
away from the alignment.
Numeric operands follow the same positional rule over the field's digits, and
both operands' decimal positions are ignored: moving 1.00 into a
three-position field with one decimal gives 10.0.
A date, time or timestamp on either side converts first and then moves. Factor 1 carries the format of the character or numeric operand, never of the date field, and must be blank when both sides are date/time types:
C *MDY MOVE chrDate dateFldDate-to-time and time-to-date are refused — go through a timestamp.
Because MOVE, MOVEL, GOTO and TAG are now lexer keywords, free-format
source can no longer use them as variable names.
COMP sets its three resulting indicators — high, low and equal — in
positions 71-72, 73-74 and 75-76. It is the only opcode for which those columns
may be non-blank.
CASxx and CABxx glue a comparison mnemonic (EQ NE LT LE GT
GE, or nothing for the unconditional form) onto the opcode. CABxx is a
comparison guarding a branch. CASxx lines chain like SELECT/WHEN — the
first true comparison runs its subroutine and the rest are skipped — and the
group is closed by ENDCS.
Both take the branch target — a label for CABxx, a subroutine name for
CASxx — in the Result field, with the two values being compared in
Factor 1 and Factor 2. The unconditional CAS/CAB forms leave both factors
blank.
C amt CABGT limit OVER
C code CASEQ 'A' SUBA
C code CASEQ 'B' SUBB
C CAS SUBOTHER
C ENDCSCALL with its PARM lines names the program in Factor 2:
C CALL 'ORD100'
C PARM custNo
C PARM totalThere is no prototype, so the callee's signature is synthesized from the
PARM operands' declared types, every parameter passed by reference. If caller
and callee disagree, the link fails — an ugly error, but not a silent one.
A named PLIST can appear anywhere in the source, including after the calls
that use it:
C CALL 'ORD100' ORDPARMS
C ORDPARMS PLIST
C PARM custNo
C PARM totalThe one ordering rule is that the PLIST must be in the same run of C-spec
lines as its CALL or a later one — a CALL in an earlier run than its
PLIST is refused by name.
PARM also supports Factor 1 and Factor 2 operands, which move data into the
parameter before the call and out of it afterwards.
A member with an *ENTRY PLIST does not compile to a main(). It becomes a
callable function taking each parameter by reference, which is exactly the
signature a caller's CALL/PARM synthesizes:
C *ENTRY PLIST
C PARM inCustNo
C PARM outTotalThe function's name comes from the source file name, upper-cased with the
directory and extension stripped — ORD100.rpgle is reachable as
CALL 'ORD100'. A file name that is not a usable symbol is refused with a
message telling you to rename the file.
Both work outside /free blocks, as a line-splicing pass that runs before
anything else, so a copied H/F/D/C line is indistinguishable from one that was
physically present:
/COPY custdefs.rpgleincThe text after the directive is a literal filename opened relative to the current working directory — there is no library or member catalog and no search path. Nesting is capped at 10 levels. Line numbers after an expansion point refer to the flattened line stream rather than the original file.
C/EXEC SQL opens the statement, C+ continues it, and C/END-EXEC closes it:
C/EXEC SQL
C+ SELECT COUNT(*) INTO :rowCount
C+ FROM customers WHERE status = 'A'
C/END-EXECThe gathered text goes to the same SQL handling free-format EXEC SQL uses, so
everything in the Embedded SQL chapter applies unchanged.
The RPG cycle is not implemented and will not be. Concretely, in fixed-format source that means:
| Rejected | Where | Note |
|---|---|---|
Control levels L0, L1-L9, LR
|
C-spec positions 7-8 | Cycle-only |
| Resulting indicators | C-spec 71-76 | Except on COMP
|
| Inline field length/decimals | C-spec 64-70 | Use a D-spec |
P-specs |
— | No procedure declaration; use a /free block |
D-spec types C, PR, PI
|
D-spec 24-25 | Constants and prototypes; use a /free block |
DO |
— | Use FOR
|
| Matching fields | I-spec 65-66 | Cycle-adjacent |
| Sequence checking | I-spec 17-20 | Must be blank |
Every one of these produces a distinct compile error naming the problem, rather than being silently ignored.
- Installation
- Quick Start
- CLI Reference
- Language Basics
- Data Types
- Variables and Constants
- Data Structures
- Arrays
- Varying-Dimension Arrays
- Control Flow
- Procedures
- Procedure Overloading
- Built-In Functions
- Error Handling
- Operation Extenders
- Enumerations
- Program Status Data Structure
- Data Areas
- SND-MSG
- Embedded SQL
- Record-Level Access
- Display Files (WORKSTN)
- Program-Described Files (I-Specs and O-Specs)
- DATA-INTO and DATA-GEN
- XML-INTO
- Fixed-Format Source
- Database Connections
- Multi-Module Programs
- Environment Variables
- Testing
- Complete Example: Employee Report
- IBM i Conformance Results
- IBM i Execution Results