Amint azt a bevezetőben említettük, a nyelv a ezen a téren is szokatlan tulajdonságokkal rendelkezik. A lyukkártyák hagyományát az RPG szintaktikája a mai napig őrzi, amennyiben szigorúan meg van határozva, hogy mit pontosan hova (sortípus, oszlop) írhatunk. (A programozó munkáját a SEU editor sorvezetők megjelenítésével ill. szintaktikai ellenőrzéssel könnyíti.) Minden sor elején meghatározzuk, hogy az aktuális sor milyen kártyának felel meg, majd az aktuális kártya pozícióbeosztásának megfelelően töltjük ki a sor további részét. A konkrét pozíciók a nyelv fejlődésével sokat változtak mindegyik kártyatípusnál. Megjegyezzük, hogy a nyelv legújabb változatában a C kártyák esetén lehetőség van bizonyos megszorításokkal a szabadon indentálhatóságra, más kérdés, hogy a tipikus RPG-programozó fog-e élni ezzel a lehetőséggel...
Jelkészlet
- Az angol ABC nagybetűi (az RPG IV elfogad kisbetűket is, amik a fordítás során naggyá konvertálódnak)
- [0-9]
- + - * , . ' & / $ # : @ _ < > = ( ) %
- a space karatker
Azonosítók
Az azonosítónevekre a szokásos szabályok vonatkoznak (alfabetikus karakterrel (+ $ # @) kezdődhetnek, alfanumerikussal folytatódhatnak (+ _), nem lehetnek foglalt szavak, és max 4096 karakter hosszúak lehetnek. (A 15 karakternél hosszabb azonosítók csak folytatósorokba férnek bele.) Érdekesség, hogy tömbnevek nem kezdődhetnek a TAB karaktersorozattal.
Literálok
- karakterliterálok: (akár 16 bites) karakterek tetszőleges sorozata aposztrófok közt
- indikátorliterálok: '1', ill. '0' (egy karakteres karakterliterál)
- hexadecimális: X'x1x2...xn'
- numerikus: decimálisan vagy E formában megadott lebegőpontos, tizedesvesszőként pont és vessző is használható
- dátum-, idő-, timestampliterálok
A nyelv sajátosságai
File-ok
Ahhoz, hogy megértsük az RPG nyelvi elemeit, először meg kell ismerkednünk a nyelvben (ill. az OS/400-ban) a file-okkal kapcsolatos alapfogalmakkal. Mindenekelőtt tudni kell, hogy az OS/400 rekordszinten végez minden I/O műveletet, ill. azt, hogy a DB/2 adatbáziskezelőrendszer olyan mélyen van integrálva a rendszerbe, hogy az OS/400 ún. adatbásisfájljai egyben adatbázistáblák is (ennek megfelelően akár SQL-en keresztül is feldolgozhatók, létrehozhatók, stb.). Továbbá file-ok létrehozásakor meg kell adnunk a file rekordszerkezetét (vagy az SQL create table paramétereiként, vagy a hagyományos interface-en keresztül az erre kitalált DDS rekordszerkezetleíró nyelv segítségével), és -- mivel az OS/400 nagymértékben objektumközpontú rendszer -- a file-ok mint *FILE típusú objektumok később is tudják a saját szerkezetüket, és ez le is kérdezhető. Végül fontos megemlíteni azt is, hogy az AS/400-ak hagyományos termináljaival (az ún. 5250-es terminálokkal) a rendszer teljes képernyős interface-eken keresztül kommunikál, ami által egy képernyő szintén tekinthető egy -- input és output mezőkből álló -- rekordszerkezetnek (sőt a képernyők megtervezésére ugyanazt a DDS-t használhatjuk, amit az adatbázistáblák rekordszerkezetének meghatározására). Ennek megfelelően az OS/400 alatt futó programok a képernyőket ugyanolyan FILE-ként kezelik, mint a fizikai fájlokat: amit a user beír, azt input mezőként látjuk abban a rekordban, amit akkor kapunk meg, ha a felhasználó leütötte az Entert (vagy valamilyen funkcióbillentyűt), amit pedig kiírunk (vagy nyomtató esetében kinyomtatunk), azt pedig output mezőként küldjük ki a képernyőre (nyomtatóra).
A fentieknek megfelelően az RPG megkülönböztet program-described ill. externally-described file-okat. Előbbieknél, mikor egy file-t bemutatunk az RPG-nek, akkor a nyelv adatdefiníciós eszközeivel le kell írnunk az adatszerkezetét. Viszont -- mivel az OS/400 file-jai pontosan tudják saját rekordszerkezetüket -- ezt a leírást megspórolhatjuk (sőt a redundancia megszüntetésével elkerülhetjük a figyelmetlen változtatásokkal könnyen előidézhető konzisztencia borulását, és az ebből származó hibalehetőségeket). Elég egyszerűen azt mondani az RPG-nek, hogy a file externally-described, és akkor a fordító a file rekordszerkezete alapján automatikusan legenerálja helyettünk a vonatkozó definíciókat. (Emiatt a file-nak nyilván már fordítási időben léteznie kell, ha externally-describednak adjuk meg.
Az adatáramlás iránya szempontjából egy file lehet:
- Input -- ezekből csak olvashatunk
- Output -- ezekbe rekordokat írhatunk
- Update -- ezekben rekordokat olvashatunk, módosíthatunk és törölhetünk
- Combined -- ezekből olvashatunk, és írhatunk, az előbbitől annyiban különbözik, hogy kiírásnál csak az output rekordnak megfelelő mezőket írja ki (ami különbözhet (pl. kevesebb lehet) az input rekordtól), míg az update file ugyanazt a rekordot írja vissza a módosított mezőkkel, amit beolvasott.
Alapvető szempont, hogy egy file-ból a rekordokat milyen sorrendben kívánjuk feldolgozni (access path). Ahogy a COBOL-ban a programozónak 3 lehetőség közül kell választania egy file definiálásakor: szekvenciálisan, relatív rekord sorszám szerint, ill. kulcsmező segítségével dolgozza fel a fájlt; az RPG-ben is hasonló módokon kezelhetjük file-jainkat. (Ezekről szintén a megfelelő input specifikációban kell nyilatkoznunk.) Az, hogy a file-unkat milyen szervezésűnek és elérésűnek definiáljuk, a használható műveleteket is meghatározza. Az opciók:
- Externally described file-ok esetén (ez az információ is a DDS-ben tárolt)
- szekvenciálisan, tárolási sorrendben (arrival sequence), azaz a rekordok abban a sorrendben érkeznek, ahogy azok a file-ba bekerülnek.
- kulccsal, a kulcsmező(ke)t a DDS tartalmazza.
- Program-described file-ok esetén a file-unk lehet
- indexelt file, amelyik az externally described kulcsolt filenak felel meg azzal a különbséggel, hogy a kulcsot adatleíró specifikációban adjuk meg. Egy indexelt file feldolgozható:
- szekvenciálisan kulcs alapján
- szekvenciálisan korlátok (limits) közt, ekkor megadjuk az elérhető rekordokat (pontosabban ezek alsó és felső határát)
- véletleneléréssel kulcs alapján
- szekvenciális file, mely feldolgozható
- szekvenciálisan, ekkor a véletlenelérésű utasítások (pl. CHAIN, SETLL, SETGT, stb.) nem használhatók
- véletleneléréssel relatív rekord sorszám szerint
- record-addrass file segítségével (lásd alább)
A felhasználás szempontjából egy file lehet:
- Primary -- ilyen legfeljebb 1 lehet egy programban, és az RPG programciklus szempontjából meghatározó jelentőségű: ennek a feldolgozása köré épül a program (ha megadunk ilyet). A file feldolgozása szekvenciális, az utolsó rekord feldolgozásakor beállítódik a *LR (last record) indikátor, ami a program terminálásához vezet. (Ezért egy RPG programból úgy tudunk kilépni, hogy a *LR indikátort beállítjuk... :-))
- Secondary -- Ha az RPG programciklus több file-t is feldolgoz, akkor többi file-t secondary-ként adjuk meg. Ezeknek olvashatónak kell lenniük (input, update vagy combined). A feldolgozásuk sorrendjét, pontosabban azt, hogy a következő feldolgozandó rekordot melyik file-ból vesszük -- ha nem adtunk meg összehasonlító mezőket (match field) -- az F kártyákon található specifikációk sorrendje, ill. -- a match fieldek használata esetén -- a programciklus logikája határozza meg. Ez utóbbi úgy történik, hogy az első programciklus (erről később) elején a program beolvas egy-egy rekordot, a primary ill. a secondary file-okból, majd összehasonlítja ezeknek a rekordoknak a match fieldjeit. (Azt, hogy melyek ezek, mi adjuk meg az input specifikációkban.) Végül a legkisebb (vagy legnagyobb) értékű mezővel rendelkező rekordot dolgozza fel a programciklusban. A következő ciklus elején a program betölt egy rekordot az előbb használt file-ból, és újra összehasnlít.
- Record Adress File (RAF) -- Ez egy (max 1.) olyan szekvenciális inputfile, amely egy másik (direkt vagy index-szekvenciális szerveződésű) file rekordjainak a kulcsát tartalmazza abban a sorrendben, ahogy fel szeretnénk dolgozni azt. A kulcs lehet a leírt file rekordjainak relatív sorrendje, vagy pedig a már tárgyalt ún. limits rekordok, amelyek rekordok alsó és felső határát tartalmazza.
- Array or Table -- az ilyen file-ok a programinicializáció során töltődnek be, és egy tömböt vagy táblát tartalmaznak.
- Full procedural -- ha egy file-t nem az RPG programciklus logikája szerint akarjuk feldolgozni, hanem a saját kezünkbe szeretnénk venni az irányítást, akkor azt full proceduralnak definiáljuk. Ekkor a file feldolgozását pl. a CHAIN és a READ utasításokkal kontrollálhatjuk.
Az RPG programciklus
Az RPG tökéletesen támogatja a szekvenciális feldolgozást. Ez a feldolgozás rekordonkénti feldolgozás, mely egy-egy input rekord beolvasásából, annak feldolgozásából, majd szükség szerinti output rekorrdok kiiratásából áll. Nem szükséges a feldolgozás minden lépését programoznunk, mert a minden rekordnál ismétlődő mechanikus lépéseket az RPG maga elvégzi.
Ez a ciklikus vezérlés csak első látásra tűnik szokatlannak: hasonló elven működnek az ismert unixos filterek (sed, awk, stb.) is.
Az RPG program végrehajtásának menete (nem túl bonyolult RPG ciklus):
1. Fejléc - detail output
2. Rekord olvasás (a primary file-ból, ez a file irányítja a feldolgozás menetét)
3. Ha "End of file", akkor vége
4. Rekordváltozók törlése
5. Kalkuláció elvégzése (C kártyák)
6. Ismételd az 1. ponttól
Mivel ennek az implicit programciklusnak a pontos ismerete alapvető és nélkülözhetetlen az RPG nyelven írt programok megértéséhez, ill. írásához, a fentiek részletezésre (és pontosításra) szorulnak (a 47 pontból álló, teljes részletességű lépéssorozat közlésére itt nem vállalkozhatunk, megtalálható a nyelv refernciájában):
- Az első lépésben az RPG feldolgozza az output specifikációk fejléc és részletszámítás-kiírási(detail) sorait (magyarul kiírja a fejlécet, és a részletszámításokhoz rendelt kimenetet). Fetch overflow logic használata esetén (lásd alább), ha a lapváltás (overflow) indikátor be van kapcsolva, akkor az RPG kiírja a vonatkozó lapváltáshoz kapcsolódó sorokat.
- Majd beolvassa a következő rekordot, de nem szükségszerűen a primary file-ból, hanem esetleg valamelyik secondary file-ból. (Azt, hogy melyikből, pl. a match field (lásd fentebb) dönti el.) Ha az előző feldolgozott rekordban meg voltak adva előreolvasó(lookahead) mezők, akkor a rekord már a tárvan van, nem történik olvasás. Az RPG megállapítja a beolvasott rekord szerkezetét, és hogy a beolvasott rekord megfelel-e a programban megadott szerkezetnek. Ha nem, akkor a kivételkezelő rutinok kapják a vezérlést (lásd a kivételkezelés résznél).
- Ezután az RPG elvégzi az összesítő (total) számításokat, ezeket a control level indikátorokkal befolyásolhatjuk (lásd alább). A számítások elvégzése kifejezés alatt azt értjük, hogy a C kártyákon megadott utasítások (op-code-ok) közül végezzük el azokat, amelyeket összesítő számításnak specifikáltunk.
- feldolgozza az összesítő kiírásokat (az output sepcifikációk total output sorait). Megvizsgálja a lapváltó indikátorokat, és ha be van valamelyik kapcsolva, akkor a lapváltó rutinra kerül a vezérlés.
- megvizsgálja az LR indikátort (lásd lejjebb). Ha bekapcsolt, akkor kilép.
- A kiválasztott input rekordok mezői a feldolgozási területre másolódnak, és az RPG bekapcsolja a mező indikátortokat.
- az RPG elvégzi a részletszámításokat (azokat, amelyeket nem jelöltünk a control level indikátorokkal). Ez -- hasonlóan a total számítások elvégzéséhez -- a megfelelő C kártyákon található utasítások elvégzését jelenti. A 2. pontban beolvasott rekord adatait használja ehhez.
- elölről
Az első és az utolsó ciklus a fentiektől egy kicsit különbözik:
Az első ciklus
- feldolgozza a paramétereket, megnyitja a file-okat, inicializálja az adatokat,
- kiírja az 1P (first page) indikátorral jelzett rekordokat (az első rekord beolvasása előtt kiírhatunk konstans-jellegű információkat, mint a dátum, vagy az oldalszám).
A programozónak lehetősége van megadni (és megírni) egy inicializációs szubrutint, amelyet a first page (1P) kiírás előtt hajtja végre az RPG.
Az utolsó ciklusban, ha nincs több beolvasandó rekord, a program bekapcsolja az LR és az L1-L9 indikátorokat, elvégzi az összesítő számításokat és kiírásokat, lezárja a file-okat, és kilép.
Ha csak full procedural file-t definiálunk, vagy egy C-kártyán a NOMAIN specifikációt tesszük, a fordító nem generál ciklust.
lapváltáskezelés
Az RPG segítséget nyújt a programozónak, hogy új lapokra fejlécet szeretnénk nyomtatni, ill. hogy elkerülje, hogy nyomtatás közben a perforációra írjon, stb. Az RPG ciklus alap lapváltáskezelője az összesítő kiírások után ellenőrzi, hogy a lapváltó indikátor be van-e kapcsolva. Ha igen, akkor a lapváltó rutin kapja a vezérlést. Ha a programozó (pl. a jobb helykihasználás reményében) jobban a kezébe akarja venni az overflow indikátor figyelését,
akkor specifikálhatja az ún. fetch overflow logic használatát: ekkor az overflow indikátort megvizsgálja a program minden olyan sor írásakor, amit fetch overflow-val specifikáltunk. (És természetesen a lapváltó rutint indítja el, ha bekapcsolt.)
A lapváltó rutin a következőt csinálja:
- a program megvizsgálja, hogy az előzőleg kiírt lapváltási sorok a fetch overflow logic-nak köszönhetően íródtak-e ki. (Azaz, hogy olyan sornál történt-e lapváltás legutóbb, amelyiknél a programozó a fetch overflowt specifikálta.) Ha igen, visszatér, ha nem akkor a 2. pontra lép.
- Kiír minden olyan output sort, amelyik az overflow indikátorral van ellátva
Az RPG űrlapjai
Minden RPG forrásprogramot speciális űrlapokra kell írni, amelyeket specifikációs lapoknak nevezünk. E lapokra kerülnek az RPG program utasításai, amelyek elterjedt neve az RPG-specifikáció. Az űrlapok egy-egy megfelelően kitöltött sora alapján készül el a forrásprogram egy-egy lyukkártyája. Minden sor 80 pozíciót tartalmaz és a sorok megfelelő pozíciószáma a mérvadó a kártya pozíciószámára nézve. A sor (egyben a kártya pozíciószáma) az űrlapokon a fejlécek alapján látható. Ötféle űrlap van, amelyeknek megfelelően hétféle programkártya lyukasztható.
- RPG vezérkártya és file-leírás Minden forrásprogram részére le kell lyukasztani egy H-kártyát (Header Information), amelyet az RPG forrásprogram vezérkártyájának nevezünk. Ugyanezen az űrlapon találhatók a file leírására szolgáló F- kártyák (File Description) specifikálását szolgáló sorok.
- RPG file-kiegészítés és sorszámlálás Szükség esetén ennek alapján kell az E-kártyát (Extension Card) lyukasztani. És ezen az űrlapon látható a nyomtatandó L-specifikáció (Line Counter).
- RPG input-adatok A forrásprogram részére szükséges beolvasásokkal kapcsolatos előírásokat tartalmazó I-típusú űrlap (Input Specification). Szükség esetén egynél több is alkalmazható belőle.
- RPG számítási műveletek A forrásprogramban kijelölésre kerülő számítási műveletek leírását tartalmazó űrlap. Ez a C-űrlap (Calculation Specification). Egynél több is alkalmazható belőle.
- RPG output-adatok Az RPG program által szolgáltatott output-adatok megjelenítésére vonatkozó előírásokat. Ez az O-űrlap (Output Specification). Egynél több is beállítható a forrásprogramba.
Az űrlapok kitöltésének általános szabályai: Minden űrlap hosszabb-rövidebb mezőkre van osztva, és a felhasználó a forrásprogram megfelelő információit írja e mezőkbe. A bejegyzéseket alfanumerikus és numerikus csoportba soroljuk. A segítségükkel felvehető jelsorozat a szimbólum, amelynek előállítása az alábbi szabályok szerint oldható meg.
- A jelsorozat legalább és legfeljebb annyi karakterből állhat, amennyi annak az adott mezőnek a hossza, ahol el akarjuk helyezni.
- Az alfanumerikus jelsorozat első karaktere csak alfabetikus lehet, a további karakterek alfanumerikusak lehetnek.
Ha a jelsorozat hossza rövidebb, mint az adott mező hossza, akkor a következő szabályok érvényesek:
- Az alfanumerikus jelsorozat balra zárt legyen, annak első karaktere a mező első pozíciójába kerüljön. A karakterek között blank nem állhat.
- Numerikus karaktersorozat (konstans) jobbra zárt legyen, azaz annak utosó karaktere a mező utolsó pozíciójába kerüljön. A karakterek között blank nem állhat.
A karakteres konstans mindazokat a jeleket is tartalmazhatja, amelyek az RPG jelkészletében nem találhatók meg, de a számítógép jelkészletében megvannak.
Kártyák leírása
A vezérkártya
Ez a kártya a 6. oszlopában kötelezően a H karaktert tartalmazza és ez a forrásprogram első feldolgozandó kártyája. Fő funkciója annak jelzése a fordítóprogram részére, hogy a forrásprogram kártyái következnek. Emellett bizonyos információkkal látja el a fordítóprogramot, amelyek részben a fordítás vezérléséhez szükségesek, részben pedig a létrehozandó tárgyprogram kialakítására vonatkozó előírásokat tartalmazzák.
A file leírása
Az egyes file-okat a file leírására szolgáló F-űrlapok segítségével definiáljuk, ahol általában egy file leírásához egy sorra van szükség, de adott esetben szükség lehet további információk megadására is, amelyeket a következőképpen adhatjuk meg:
- Az F-specifikációban a file-ra utaló további sorokban folytatható a definíció. Ezek alapján folytatássorok jelennek meg a forrásprogram leírásában és a programban folytatáskártyák találhatók.
- A file-ra vonatkozóan további információkat filekiegészítés E-specifikációjában adjuk meg.
- A file-ra vonatkozó további információkat a sorszámlálás L-specifikációi között állítjuk össze.
Egy file-lal kapcsolatban vagy csak E-, vagy csak L-specifikáció alkalmazható. A file-típusa lehet:
- Input-file, jele I. Az ilyen típusra csak beolvasási műveletek alkalmazhatók
- Output-file, jele O. Csak kiírási műveletek alkalmazhatók rá
- Kombinált-file, jele C. Minden olyan lyukkártyaolvasó-lyukasztóval kapcsolatban jöhet számításba, amely fizikailag lehetővé teszi a kártya beolvasását követően az eredmény lyukasztását ugyanabba kártyába
- Update-file, jele U. minden olyan nem kombinált file amellyel kapcsolatban mind beolvasási mind kiírási műveletek előfordulnak. Csak közvetlen elérésű tárak esetén jöhet számításba.
A periféria adottságaitól függ, hogy milyen file-típus felvételére alkalmas.
- lyukkártyaolvasó: I
- lyukkártyalyukasztó: O
- lyukkártyaolvasó-lyukasztó: I, O, C
- sornyomtató: O
- mágnesszalag: I, O
- mágneslemez: I, O, U
A file-okat jellegük szerint csoportosíthatjuk:
- elsődleges file (P, primary) ez a legfontosabb, ennek feldolgozása kihat a többi file feldolgozására is. Több file esetén a kijelölt elsődleges file irányítja a feldolgozás menetét. Feldolgozása csak szekvenciálisan mehet végbe. Ha a program csak egy file-t tartalmaz akkor az csak elsődleges lehet.
- Másodlagos file (S, secondary) ha jellege nem elsődleges és nem is sorolható a többi kategóriába sem. Az ilyen file-ok kezelését az elsődleges file vezérli.
- Láncolt file (C, chain) feldolgozása szorosan összefügg egy másik file feldolgozásával. A kapcsolatban a két file egymás alá van rendelve. A feldolgozást irányító file a vezérlőfile, és amelyiket irányítja az a vezérelt file.
- Rekordcím file (R, record address) egy másik index-szekvenciális vagy direkt szervezésű file rekordjainak a kulcsait tartalmazza.
- Táblázatos file (T) vagy más néven tömbfile, a feldolgozás részére szükséges táblázatot vagy tömböt tartalmazza.
- Speciális file (D, demand) olyan input-, update- vagy kombinált file, amelyet szekvenciálisan olvasunk be.
A file rendeltetésének kódját a file-leírás F-űrlapján kell feltűntetni.
Az I-űrlap
A beolvasási műveleteket írja elő. Ezen a lapon kell specifikálni minden olyan file-t amellyel kapcsolatban beolvasásra kell számítani.
A C-űrlap
Ezen a lapon kell specifikálni minden nem input és nem output műveletet. Ezek azok a specifikációk amelyek leginkább megfelelnek a magas szintű nyelvek utasítás fogalmának.
Az O-űrlap
A kiírási műveletek leírására használhatjuk. Ezen specifikálunk minden eredményt szolgáltató műveletet, pl. sornyomtató vagy aktualizálandó file részére.