Le cycle GAP du RPG, la boucle qu'on ne voit pas
Un programme RPG peut lire tout un fichier, imprimer un état avec ses sous-totaux et s'arrêter
seul, sans une seule boucle ni un seul READ. C'est le cycle GAP. Voici d'où il vient,
pourquoi il existe, et ce qu'il fait vraiment, relevé pas à pas dans le débogueur STRISDB.
En une phrase
Le cycle GAP (ou cycle RPG, RPG program cycle en anglais) est la boucle que le compilateur ajoute de lui-même à un programme RPG qui déclare un fichier primaire : lire un enregistrement, fermer le groupe précédent s'il est terminé (calculs et impression des totaux), traiter l'enregistrement, et recommencer jusqu'à la fin du fichier.
D'où ça vient
Avant les ordinateurs, la gestion se faisait avec des cartes perforées et des tabulatrices : des machines comptables qui lisaient un paquet de cartes trié, additionnaient des montants et imprimaient un état. On ne les programmait pas en écrivant du code, mais en câblant un tableau de connexions. Leur fonctionnement était un cycle machine : une carte entre, on cumule, on imprime ; et quand le numéro de client change sur la carte suivante, on imprime le sous-total avant de continuer.
Le 5 octobre 1959, IBM annonce l'IBM 1401, un ordinateur pensé pour remplacer ces machines. Pour lui naissent deux générateurs d'états : FARGO, puis le RPG, pour Report Program Generator (« générateur de programmes d'états »), qui le remplace vite. Les sources ne s'accordent pas sur l'année exacte du RPG : l'encyclopédie Wikipédia retient 1959, des anciens programmeurs du 1401 le situent vers 1961. Le but, lui, est connu : que les techniciens des tabulatrices passent à l'ordinateur sans tout réapprendre. Le RPG reprend donc le cycle de la machine : le programmeur remplit des feuilles de programmation en colonnes (quel fichier, quelles zones, quels totaux, quelle mise en page) et le compilateur génère la boucle autour.
En France, le RPG s'est appelé le GAP, pour « Générateur Automatique de Programmes ». Le nom apparaît avec le System/3 : on parlait de GAP II sur les System/3, /34 et /36, puis de GAP III. Le manuel de Max Rémy paru chez Masson s'intitule d'ailleurs « Générateur automatique de programme : le langage GAP/RPG ». D'où le nom de cycle GAP, que l'on entend encore dans les équipes IBM i.
- 1959IBM 1401, FARGO puis RPGL'IBM 1401 est annoncé en octobre ; FARGO, puis le RPG au tout début des années 1960, y reprennent le cycle des tabulatrices.
- 1969RPG II (GAP II)Vers 1969, sur le System/3, puis les System/32, /34 et /36. Le cycle est au cœur du langage.
- 1978RPG IIISur le System/38, annoncé en octobre 1978 et commercialisé en 1979 : fichiers décrits en externe, opérations structurées (IF, DO). On peut écrire tout un programme sans le cycle.
- 1988RPG/400Le RPG III de l'AS/400, celui des exemples de cette page.
- 1994RPG IV (ILE RPG)Noms longs, fonctions intégrées, procédures. Le cycle reste disponible.
- 2013RPG en format libre (7.1, par PTF)Les spécifications H, F, D et P s'écrivent en format libre (CTL-OPT, DCL-F, DCL-S…). Les spécifications I et O, celles du cycle, restent en colonnes.
- 2015**FREEPar PTF sur 7.1 et 7.2, d'origine en 7.3 : le code peut commencer en colonne 1 et dépasser la colonne 80. C'est le « full free ».
Pourquoi c'était comme ça
Le cycle n'est pas une bizarrerie : il collait exactement aux contraintes de l'époque.
- Les données arrivaient dans l'ordre. Un paquet de cartes, puis une bande magnétique, se lisent du début à la fin, sans revenir en arrière. Lire l'enregistrement suivant était la seule opération possible : inutile de la faire écrire au programmeur.
- Le travail, c'était l'état imprimé. Factures, relevés, paie, statistiques : presque tous les programmes lisaient un fichier trié et imprimaient des lignes avec des sous-totaux par client, par agence, par mois. Le cycle et ses ruptures font exactement ça.
- La mémoire se comptait en milliers de caractères. L'IBM 1401 existait en six tailles, de 1 400 à 16 000 caractères. Une logique toute faite, générée par le compilateur, prenait moins de place et de temps que des programmes écrits à la main.
- Les mêmes gens, la même logique. Les opérateurs de tabulatrices retrouvaient dans le cycle le fonctionnement de leurs machines, et les feuilles en colonnes ressemblaient à leurs tableaux de connexions.
Le revers : la logique du programme est en partie invisible. Ce qui se passe dépend de l'ordre des étapes du cycle, que l'on ne lit nulle part dans le source. C'est pour cela qu'il déroute tant les développeurs d'aujourd'hui.
Le cycle en 7 étapes
À chaque enregistrement, le programme repasse par les mêmes étapes, toujours dans cet ordre. C'est l'ordre que décrit le manuel de référence du RPG d'IBM, et c'est celui que l'on observe dans le débogueur (niveau 2).
- Sorties d'en-tête et de détail. Lignes H et D des spécifications O. Au premier cycle, seules les lignes 1P (en-têtes) s'impriment.
- Lecture d'un enregistrement. Le fichier primaire (IP) est lu sans instruction READ. Le cycle compare les zones de contrôle : si elles changent, L1 (à L9) s'allume. En fin de fichier, LR s'allume.
- Calculs de total. Les calculs marqués L1… L9 ou LR en colonnes 7-8. Ils voient encore les valeurs du groupe précédent. Sautés au premier enregistrement.
- Sorties de total. Les lignes T. Puis, si la page est pleine (indicateur OF), les lignes de débordement : nouvel en-tête.
- LR est allumé ?. Si oui, le programme ferme ses fichiers et s'arrête. Sinon, il continue.
- Transfert des zones. Les zones de l'enregistrement lu à l'étape 2 passent dans les variables du programme.
- Calculs de détail. Les calculs sans indicateur de niveau. Puis retour à l'étape 1.
Retenez surtout ceci : le total d'un groupe se calcule et s'imprime après la lecture du premier enregistrement du groupe suivant, mais avant que ses zones n'arrivent dans les variables. Tout le reste en découle.
L'allégorie de la caissière
Imaginez une caissière de supermarché à l'ancienne, avec son tapis roulant et ses barres « client suivant ». Elle n'a pas à réfléchir à l'ordre de son travail : il est toujours le même, comme le cycle.
Elle prend l'article qui arrive et le bipe. Elle prend le suivant et le bipe. Un jour, l'article qu'elle saisit est une barre de séparation : le client précédent a fini. Avant de biper quoi que ce soit du nouveau client, elle imprime le ticket de l'ancien, avec ses articles et son total. Puis elle repart à zéro et bipe le premier article du nouveau client. Au premier client de la journée, elle n'imprime aucun ticket avant : il n'y a personne à encaisser. Le soir, le magasin ferme : elle imprime le ticket du dernier client, puis le ticket Z avec le total de la journée.
| À la caisse | Dans le cycle | Ce qu'il faut comprendre |
|---|---|---|
| Le tapis roulant qui apporte les articles | Le fichier primaire | La caissière ne va pas chercher les articles : ils arrivent seuls, un par un. Pas de READ. |
| Biper un article | Les calculs de détail | Ajouter le prix au ticket en cours. |
| La barre « client suivant » posée sur le tapis | La zone de contrôle (L1) | Elle ne la voit qu'en prenant l'article suivant : la rupture se découvre à la lecture. |
| Imprimer le ticket du client qui part | Calculs et sorties de total | Elle le fait avant de biper l'article du nouveau client, avec les montants de l'ancien. |
| Pas de ticket avant le premier client | Pas de total au premier enregistrement | Il n'y a encore personne à encaisser. |
| Changer le rouleau de papier | Le débordement (OF) | Nouvelle page, on réimprime l'en-tête. |
| La fermeture du magasin | LR | Dernier ticket (le dernier groupe), puis le total de la journée, puis on éteint. |
Niveau 1 Découvrir : un programme sans boucle
Lire et imprimer un fichier sans écrire de boucle
Le fichier de démonstration contient six ventes fictives, triées par région. Sa description DDS :
A R CYCVTR TEXT('Ventes par client')
A REGION 10A TEXT('Region')
A CLIENT 20A TEXT('Client')
A MONTNT 7P 2 TEXT('Montant')
Voici un programme RPG/400 complet qui imprime ces six ventes. Lisez-le : il n'y a aucune spécification de calcul (ligne C), donc ni boucle, ni lecture, ni test de fin de fichier.
H
F* Niveau 1 : le cycle lit, imprime et s'arrete tout seul
FCYCVTE IP E DISK
FQSYSPRT O F 132 OF PRINTER
ICYCVTR 01
OQSYSPRT H 201 1P
O OR OF
O 24 'VENTES PAR CLIENT'
O D 1 01
O REGION 12
O CLIENT 34
O MONTNTJ 46
IP(ligne F de CYCVTE)- I pour entrée, P pour primaire : c'est ce fichier que le cycle lit tout seul. C'est cette lettre P qui met le cycle en route.
01(ligne I)- Indicateur allumé par le cycle chaque fois qu'il lit un enregistrement du format CYCVTR.
H … 1P- Ligne d'en-tête imprimée au premier cycle (1P, first page) et à chaque nouvelle page (OF).
D … 01- Ligne de détail, imprimée à chaque cycle où un enregistrement a été lu.
Résultat réel, compilé par CRTRPGPGM et exécuté sur IBM i 7.5 (spoule QSYSPRT) :
VENTES PAR CLIENT
NORD DUPONT 120.00
NORD MARTIN 80.50
OUEST DURAND 75.00
SUD BERNARD 200.00
SUD PETIT 50.00
SUD ROBERT 30.00
Le programme s'est arrêté seul : à la lecture qui suit ROBERT, le cycle a trouvé la fin du fichier, allumé LR (last record) et terminé le programme.

Dans le débogueur, la touche F5 (pas à pas) montre ce que le cycle exécute. Pour CYCLE1, les arrêts
se succèdent ainsi : 600 800 900 500 700 500 600 900 1000 1100 1200 …
L'instruction 500 (la ligne I) revient deux fois par cycle : une fois pour la lecture, une fois pour
le transfert des zones. Et chaque ligne de détail s'imprime au début du cycle
suivant (600 à 1200), juste avant la lecture d'après.
À retenir au niveau 1
- Un fichier déclaré
IPest lu par le cycle : pas deREAD. - Le cycle tourne tant qu'il reste des enregistrements : pas de boucle.
- En fin de fichier, il allume LR et arrête le programme : pas de test de fin.
Niveau 2 Maîtriser : ruptures, totaux et ordre exact
Sous-totaux par région : la rupture de contrôle L1
Ajoutons des sous-totaux par région, un compteur de clients et un total général. Toujours sans boucle :
H
F* Niveau 2 : rupture L1 sur la region, totaux et LR
FCYCVTE IP E DISK
FQSYSPRT O F 132 OF PRINTER
ICYCVTR 01
I REGION REGIONL1
C* Calculs de detail : a chaque enregistrement lu
C 01 TOTREG ADD MONTNT TOTREG 92
C 01 NBCLI ADD 1 NBCLI 30
C* Calculs de total : au changement de region
CL1 TOTGEN ADD TOTREG TOTGEN 112
OQSYSPRT H 201 1P
O OR OF
O 24 'VENTES PAR REGION'
O D 1 01
O REGION 12
O CLIENT 34
O MONTNTJ 46
O T 2 L1
O 19 'TOTAL'
O REGION 30
O TOTREGJB 46
O NBCLI ZB 52
O 62 'CLIENT(S)'
O T 1 LR
O 27 'TOTAL GENERAL'
O TOTGENJ 46
Numéros d'instruction : STRISDB numérote les lignes du membre source de 100 en 100. La 5e ligne (la ligne I) est l'instruction 500, la 11e (le calcul de total) l'instruction 1100, et ainsi de suite.
REGION … L1(ligne I)- REGION devient zone de contrôle de niveau 1 : quand sa valeur change d'un enregistrement à l'autre, le cycle allume L1.
- Lignes C sans niveau (800, 900)
- Calculs de détail, faits à chaque enregistrement (indicateur 01).
CL1(1100)- Calcul de total, fait seulement quand L1 est allumé : on ajoute le total de la région au total général.
- Lignes
T … L1etT … LR - Sorties de total. Le
B(blank after) remet TOTREG et NBCLI à zéro après l'impression.
Résultat réel, compilé par CRTRPGPGM et exécuté sur IBM i 7.5 (spoule QSYSPRT) :
VENTES PAR REGION
NORD DUPONT 120.00
NORD MARTIN 80.50
TOTAL NORD 200.50 2 CLIENT(S)
OUEST DURAND 75.00
TOTAL OUEST 75.00 1 CLIENT(S)
SUD BERNARD 200.00
SUD PETIT 50.00
SUD ROBERT 30.00
TOTAL SUD 280.00 3 CLIENT(S)
TOTAL GENERAL 555.50
Le cycle pas à pas : la mémoire à chaque étape
Avancez étape par étape dans CYCLE2 et regardez ce que le cycle fait de la mémoire : l'enregistrement qui attend dans le tampon d'entrée, les variables du programme, les indicateurs 1P, 01, L1, OF et LR, et l'état qui s'imprime. Les lignes du source en vert sont celles que le cycle exécute, celles barrées sont examinées puis sautées. Les 47 étapes suivent l'ordre relevé dans STRISDB, et l'état obtenu est celui du spoule réel.
Cycle 1 · étape 1 sur 7 : Sorties d'en-tête et de détail
100 H 200 F* Niveau 2 : rupture L1 sur la region, totaux et LR 300 FCYCVTE IP E DISK 400 FQSYSPRT O F 132 OF PRINTER 500 ICYCVTR 01 600 I REGION REGIONL1 700 C* Calculs de detail : a chaque enregistrement lu 800 C 01 TOTREG ADD MONTNT TOTREG 92 900 C 01 NBCLI ADD 1 NBCLI 301000 C* Calculs de total : au changement de region1100 CL1 TOTGEN ADD TOTREG TOTGEN 1121200 OQSYSPRT H 201 1P1300 O OR OF1400 O 24 'VENTES PAR REGION'1500 O D 1 011600 O REGION 121700 O CLIENT 341800 O MONTNTJ 461900 O T 2 L12000 O 19 'TOTAL'2100 O REGION 302200 O TOTREGJB 462300 O NBCLI ZB 522400 O 62 'CLIENT(S)'2500 O T 1 LR2600 O 27 'TOTAL GENERAL'2700 O TOTGENJ 46
Tampon d'entrée (l'enregistrement lu)
| REGION | — |
|---|---|
| CLIENT | — |
| MONTNT | — |
Variables du programme
| REGION | (blancs) |
|---|---|
| CLIENT | (blancs) |
| MONTNT | 0,00 |
| TOTREG | 0,00 |
| NBCLI | 0 |
| TOTGEN | 0,00 |
Indicateurs
- 1P allumé
- 01 éteint
- L1 éteint
- OF éteint
- LR éteint
Premier cycle : 1P (first page) est allumé, la ligne d'en-tête s'imprime. La ligne de détail est examinée, mais 01 est éteint : rien n'a encore été lu.
État imprimé
VENTES PAR REGION
NORD DUPONT 120.00
NORD MARTIN 80.50
TOTAL NORD 200.50 2 CLIENT(S)
OUEST DURAND 75.00
TOTAL OUEST 75.00 1 CLIENT(S)
SUD BERNARD 200.00
SUD PETIT 50.00
SUD ROBERT 30.00
TOTAL SUD 280.00 3 CLIENT(S)
TOTAL GENERAL 555.50
Le déroulé réel, relevé dans STRISDB
Nous avons lancé STRISDB PGM(CYCLE2) et appuyé sur F5 jusqu'à la fin du programme :
98 arrêts. Le débogueur s'arrête sur chaque ligne que le cycle examine, y compris
celles dont la condition est fausse. Voici ces arrêts, découpés en cycles :
| Cycle | Instructions où ISDB s'arrête | Ce qui se passe |
|---|---|---|
| 1 | 1200 1400 1500 500 1300 500 800 900 |
Premier cycle : les lignes 1P impriment l'en-tête. Lecture de DUPONT (NORD). Pas de total : il n'y a pas encore de groupe à fermer. Transfert des zones, puis calculs de détail. |
| 2 | 1200 1500 1600 1700 1800 500 1100 1900 2500 1300 500 800 900 |
La ligne de détail de DUPONT s'imprime. Lecture de MARTIN : même région, pas de rupture. Les lignes de total sont examinées mais pas exécutées. |
| 3 | 1200 1500 1600 1700 1800 500 1100 1900 2000 2100 2200 2300 2400 2500 1300 500 800 900 |
MARTIN s'imprime. Lecture de DURAND (OUEST) : la région change, L1 s'allume. Calcul de total, puis impression de « TOTAL NORD » (2000 à 2400). Ensuite seulement, les zones d'OUEST arrivent dans les variables. |
| 4 | 1200 1500 1600 1700 1800 500 1100 1900 2000 2100 2200 2300 2400 2500 1300 500 800 900 |
DURAND s'imprime. Lecture de BERNARD (SUD) : nouvelle rupture, « TOTAL OUEST » s'imprime. |
| 5 | 1200 1500 1600 1700 1800 500 1100 1900 2500 1300 500 800 900 |
BERNARD s'imprime. Lecture de PETIT : même région, pas de rupture. |
| 6 | 1200 1500 1600 1700 1800 500 1100 1900 2500 1300 500 800 900 |
PETIT s'imprime. Lecture de ROBERT : même région. |
| 7 | 1200 1500 1600 1700 1800 1100 1900 2000 2100 2200 2300 2400 2500 2600 2700 |
ROBERT s'imprime. La lecture suivante tombe sur la fin du fichier : LR s'allume, et avec lui L1. Total de SUD (1100, 1900 à 2400), puis total général (2500 à 2700), puis fin du programme. Plus d'arrêt en 500 : il n'y a plus rien à lire. |
Repères : 500 = la ligne I (lecture, puis transfert des zones), 800-900 = calculs de détail, 1100 = calcul de total, 1200-1800 = en-tête et détail, 1300 = débordement (OF), 1900-2400 = total de région, 2500-2700 = total général.

La preuve : au moment du total, les variables ont encore l'ancien groupe
Un point d'arrêt sur la ligne 1100 (BREAK 1100, puis F17 pour exécuter) arrête le
programme au calcul de total. Au deuxième arrêt, le cycle vient de lire DURAND, de la région OUEST :
c'est ce changement de région qui a allumé L1. Pourtant, F11 sur REGION affiche
NORD, et TOTREG vaut 200,50, le total de NORD.



On avance ensuite en pas à pas : les lignes 2000 à 2400 impriment « TOTAL NORD », 1300 teste le
débordement, puis 500 transfère enfin les zones de DURAND. Arrivé au calcul de détail (800), REGION
vaut OUEST et TOTREG a été remis à zéro par le B de la sortie de
total.



C'est la caissière qui imprime le ticket de l'ancien client avant de biper l'article du nouveau. En fin de fichier, la fenêtre « Fin du programme » apparaît après l'instruction 2700, la dernière ligne du total général :

Les pièges du niveau 2
- Pas de total au premier enregistrement. Le premier enregistrement allume bien L1 (sa région diffère de « rien »), mais le cycle saute les calculs et les sorties de total à ce moment-là. Un calcul L1 n'est donc pas un bon endroit pour initialiser quelque chose avant le premier groupe : utilisez 1P ou les calculs de détail conditionnés par L1.
- Au total, les variables appartiennent au groupe qui se termine. Un calcul de total qui utilise une zone de l'enregistrement lu (MONTNT, CLIENT) prend celle du dernier enregistrement du groupe précédent, pas celle du nouvel enregistrement.
- LR allume aussi L1 à L9. En fin de fichier, tous les niveaux sont fermés : le dernier groupe a son total (TOTAL SUD) avant le total général. Inutile de le coder à part.
- Le débogueur s'arrête sur des lignes qui ne s'exécutent pas. ISDB s'arrête sur 1100 à chaque cycle, même sans rupture : il montre la ligne que le cycle examine, la condition L1 est testée ensuite. Seuls les arrêts sur 2000 à 2400 prouvent qu'un total s'imprime.
- Une zone éditée prend plus de place qu'on ne le croit. En écrivant ce programme, le nombre de clients était d'abord placé en position 35 : il n'apparaissait jamais. TOTREG (9 chiffres) édité par le code J occupe 13 positions, de 34 à 46, et recouvrait le compteur, sans aucun message du compilateur.
- Une zone de contrôle d'un fichier décrit en externe s'écrit en deux fois. En RPG/400, la ligne I doit porter le nom externe en positions 21-30 et le nom RPG en 53-58, puis L1 en 59-60. Sans le nom en 53-58, la compilation échoue (QRG4119) ; sans le nom externe, aussi (QRG4078).
Le même programme sans cycle
Pour mesurer ce que le cycle fait à votre place, voici le même état en RPG IV full free, avec une boucle explicite. Il a été compilé par CRTBNDRPG et exécuté sur le même fichier : son état imprimé est identique, ligne pour ligne, à celui de CYCLE2.
**free
// Le même état que CYCLE2, sans le cycle : tout ce qu'il faisait est écrit à la main
ctl-opt dftactgrp(*no) actgrp(*new);
dcl-f CYCVTE usage(*input); // plus de IP : on lit soi-même
dcl-f QSYSPRT printer(132);
dcl-ds ligne len(132);
zone char(132) pos(1);
end-ds;
dcl-s regionPrec char(10);
dcl-s totReg packed(9:2);
dcl-s nbCli packed(3:0);
dcl-s totGen packed(11:2);
dcl-s premier ind inz(*on);
imprimer(8 : 'VENTES PAR REGION'); // ce que faisait la ligne H conditionnée par 1P
imprimer(1 : '');
read CYCVTR; // étape 2 du cycle : lire
dow not %eof(CYCVTE);
if not premier and REGION <> regionPrec;
totalRegion(); // la rupture L1, détectée à la main
endif;
premier = *off;
regionPrec = REGION;
totReg += MONTNT; // calculs de détail
nbCli += 1;
zone = *blanks; // sortie de détail
%subst(zone : 3 : 10) = REGION;
%subst(zone : 15 : 20) = CLIENT;
%subst(zone : 37 : 10) = %editc(MONTNT : 'J');
write QSYSPRT ligne;
read CYCVTR;
enddo;
if not premier; // à LR, le cycle fait aussi le dernier L1
totalRegion();
endif;
zone = *blanks;
%subst(zone : 15 : 13) = 'TOTAL GENERAL';
%subst(zone : 32 : 15) = %editc(totGen : 'J');
write QSYSPRT ligne;
*inlr = *on;
return;
dcl-proc imprimer;
dcl-pi *n;
colonne int(10) const;
texte varchar(132) const;
end-pi;
zone = *blanks;
if %len(texte) > 0;
%subst(zone : colonne : %len(texte)) = texte;
endif;
write QSYSPRT ligne;
end-proc;
dcl-proc totalRegion;
totGen += totReg; // ce que faisait la ligne de calcul L1
zone = *blanks;
%subst(zone : 15 : 5) = 'TOTAL';
%subst(zone : 21 : 10) = regionPrec;
%subst(zone : 34 : 13) = %editc(totReg : 'J');
%subst(zone : 50 : 3) = %editc(nbCli : 'Z');
%subst(zone : 54 : 9) = 'CLIENT(S)';
write QSYSPRT ligne;
imprimer(1 : '');
totReg = 0; // le B (blanc après) des spécifications O
nbCli = 0;
end-proc;
Tout ce que le cycle cachait devient visible : la lecture (read), la boucle
(dow not %eof), la détection de rupture (comparer REGION à la valeur précédente), le cas
du premier enregistrement (premier), le dernier total à la fin du fichier, et la remise à
zéro. Plus long, mais lisible par n'importe quel développeur.
Le cycle aujourd'hui
- Il existe toujours. Les programmes RPG II, RPG III et RPG/400 à cycle tournent
sur IBM i 7.5 et 7.6, et le RPG IV le connaît encore : il suffit d'une spécification F en
colonnes déclarant un fichier
IP. La déclaration libreDCL-Fn'a pas d'option pour un fichier primaire. - Il explique le
*INLR = *ONde tous les programmes. Sans fichier primaire, rien n'allume LR : si le programme ne le fait pas lui-même, ou ne sort pas parRETURN, le cycle relance ses calculs indéfiniment. AvecRETURNsans LR, le programme s'arrête mais garde ses fichiers ouverts et ses variables pour l'appel suivant. - On peut s'en passer complètement. Un programme écrit avec
CTL-OPT MAIN(…)ou un moduleNOMAINn'a pas de cycle du tout : ni LR à allumer, ni boucle cachée. - Faut-il réécrire l'existant ? Pas par principe : ces programmes marchent. On les réécrit en boucle explicite quand on doit les faire évoluer souvent, pour que tout le monde comprenne leur logique. Le savoir lire reste indispensable en maintenance.
Sources
Le fonctionnement du cycle et les exemples ont été vérifiés sur un IBM i 7.5 (voir la méthode). Les faits historiques et les règles du langage viennent de ces sources, consultées le 12 octobre 2026 :
- General RPG IV Program Cycle, IBM, ILE RPG Reference (IBM i 7.5). Les 7 étapes du cycle, les totaux sautés au premier cycle, LR et L1 à L9 allumés en fin de fichier.
- Program Flow in RPG Modules: Cycle Versus Linear, IBM, ILE RPG Reference (IBM i 7.5). Un module avec MAIN ou NOMAIN ne contient pas la logique du cycle.
- Equivalent Free-form Coding for Fixed-Form File Entries, IBM, ILE RPG Reference (IBM i 7.5). En format libre, un fichier est toujours entièrement procédural ou de sortie : DCL-F ne déclare jamais de fichier primaire.
- Free-form H, F, D and P statements for RPG, IBM Support, RPG Cafe. Le format libre des spécifications H, F, D et P, livré par PTF sur 7.1.
- IBM RPG, Wikipédia (en anglais). RPG présenté en 1959 pour l'IBM 1401, FARGO, l'héritage des tabulatrices, RPG II vers 1969, RPG IV en 1994, format libre en novembre 2013.
- RPG and FARGO on the 1401, IBM 1401 Restoration Project (Computer History Museum). Témoignages d'anciens programmeurs du 1401, qui situent FARGO et le RPG vers 1961.
- IBM 1401, Wikipédia (en anglais). Annonce le 5 octobre 1959, six tailles de mémoire, de 1 400 à 16 000 caractères.
- IBM System/38, Wikipédia (en anglais). Annonce le 24 octobre 1978, commercialisation en août 1979.
- Générateur automatique de programmes, Wikipédia. GAP, nom français du RPG, apparu avec le System/3 ; GAP II, puis GAP III.
- Générateur automatique de programme : le langage GAP/RPG, Max Rémy, Masson (collection ABC des langages). Manuel en français qui associe les noms GAP et RPG.
Questions fréquentes
Qu'est-ce que le cycle GAP ?
C'est la boucle que le compilateur RPG ajoute de lui-même à un programme qui déclare un fichier primaire : lire un enregistrement, détecter les ruptures, faire les calculs et les impressions de total, puis ceux de détail, et recommencer jusqu'à la fin du fichier. GAP (Générateur Automatique de Programmes) est le nom que le RPG a porté en France.
Pourquoi met-on *INLR = *ON à la fin des programmes RPG ?
LR (Last Record) est l'indicateur qui arrête le cycle. Avec un fichier primaire, le cycle l'allume seul en fin de fichier. Sans fichier primaire, rien ne l'allume : si le programme ne le fait pas (ou ne fait pas RETURN), le cycle relance les calculs indéfiniment.
Le cycle existe-t-il encore en RPG IV et en full free ?
Oui : un programme RPG IV qui déclare un fichier primaire (IP) en spécification F en colonnes tourne avec le cycle. La déclaration libre DCL-F ne sait pas déclarer de fichier primaire, et un programme écrit avec CTL-OPT MAIN ou dans un module NOMAIN n'a pas de cycle.
Pourquoi le total d'un groupe s'imprime-t-il avec les valeurs du groupe précédent ?
Parce que le cycle fait les calculs et les sorties de total juste après avoir lu l'enregistrement qui provoque la rupture, mais avant d'en transférer les zones dans les variables du programme. Les variables contiennent donc encore les valeurs du groupe qui se termine.
Faut-il réécrire les programmes à cycle ?
Pas forcément : ils fonctionnent toujours sur IBM i 7.5 et 7.6. On les réécrit en boucle explicite (READ, DOW) quand on doit les faire évoluer souvent, pour que leur logique soit visible de tous les développeurs.