Ultimo aggiornamento: 25-Ago-2020

Liberiamo l’RPG!

Dalla release IBM i 5.1 è disponibile il formato libero per scrivere programmi RPG IV.

Ad ogni release il formato libero è sempre stato arricchito fino a giungere nella versione IBM i 7.2 TR3 e 7.1 TR11 (nov-2015) a coprire tutti i tipi di specifiche e consentire l’uso del formato “totalmente libero”.

Nella versione 7.1 TR7 (nov-2013) era stato introdotto il formato libero per le specifiche di controllo e definizione.

Il formato libero è il FUTURO dell’RPG.

Scrivere codice in formato libero – dopo qualche tempo di pratica – è sicuramente più veloce, più chiaro, più moderno!

Storia in pillole

  • V5R1: specifiche C
  • V5R3: embedded SQL
  • 7.1 TR7: specifiche H, F, D, P
  • 7.1 TR11 / 7.2 TR3: formato totalmente libero

Vantaggi

  1. maggior coerenza
  2. più facile da manutenere
  3. è possibile indentare il codice
  4. c’è molto più “spazio” per scrivere codice
  5. alcune nuove caratteristiche sono disponibili sono nel formato libero
  6. l’editor LPEX di Rational Developer for i consente di convertire codice da formato fisso a formato libero
  7. molti esempi di codice nella letteratura web sono scritti in formato libero
  8. molto simile ad altri linguaggi di programmazione e quindi più facile da imparare per chi NON conosce l’RPG

Scuse “ridicole” per non usare il formato libero

  • “Finché non si rompe, non lo aggiusto”
  • “Penso che il formato fisso sia più facile da leggere”
  • “Il SEU mi evidenzia come errata la sintassi”
  • “C’è molto lavoro da fare per convertire da formato fisso a formato libero”
  • “Il mio capo non vuole che uso il formato libero perché non lo capisce”

Best practices

  1. usare i prototipi invece dei parametri
  2. evitare l’uso degli indicatori numerici e privilegiare le variabili di tipo ind (n)
  3. indentare il codice
  4. scrivere TUTTO in formato libero
  5. usare le parentesi
  6. usare coerentemente maiuscole e minuscole
    • Variabili: minuscolo con iniziale maiuscola dei vari componenti (p.es QtaOrdinata)
    • Costanti: tutto maiuscolo
    • codici operativi: tutto minuscolo
    • keyword sql: tutto minuscolo
    • nomi campi e file in SQL: tutto maiuscolo
  7. usare le espressioni
  8. utilizzare RDi (Rational Developer for i)
  9. utilizzare le direttive /COPY o /INCLUDE

Regole principali del formato libero

  • deve essere compreso tra due righe che delimitano l’inizio e la fine delle istruzioni scritte in formato libero (non più necessario da 7.1 TR7 perché ogni riga con le colonne 6 e 7 vuote e con le colonne da 8 a 80 piene sono considerate scritte in formato free; viceversa ogni riga con le colonne 6 e 7 piene è considerata scritta in formato fisso)
/free
 ...codice...
/end-free
  • il codice deve essere scritto tra le colonne 8 e 80
  • la sequenza di scrittura del codice è sempre
    op-code(ext) factor1 factor2 result;
  • tutte le istruzioni devono terminare con un ; (punto e virgola). Si può scrivere un’istruzione su più righe senza nessun carattere di continuazione, perché la fine dell’istruzione è determinato dalla presenza del ;
  • ATTENZIONE: non tutti i codici operativi possono essere scritti in formato libero: ADD, ANDxx, CABxx, CALL, CASxx, CAT, COMP, DEFINE, DIV, DOUxx, DOWxx, GOTO, IFxx, MOVE, MOVE(P), MOVEA, MOVEL, MOVEL(P), MULT, MVR, ORxx, PARM, PLIST, SCAN, SETOFF, SETON, SUB, SUBST, SUBST(P), TAG, TESTB, TESTN, TIME, WHENxx, ZADD, ZSUB.
  • I codici operativi non gestiti dal formato libero possono essere definiti “obsoleti” e “deprecabili”
  • la scrittura del codice operativo EVAL è facoltativa
  • non esistono indicatori risultanti e di condizionamento. Gli indicatori si referenziano con *INxx
  • Il codice operativo CALL deve essere sostituito con CALLP e i parametri devono essere convertiti nella struttura dei prototipi
  • le istruzioni SQL devono essere precedute dalla keyword exec sql e terminare anch’esse sempre con un ;

per le specifiche di controllo e definizione (spec. H, F, D, P)

  • le specifiche F e D possono essere mischiate
  • il tipo specifica è sostituito da un codice di dichiarazione:
    • spec. H: ctl-opt
    • spec. F (file): dcl-f
    • spec. D (prototipi): dcl-pi, dcl-pr ... end-pr
    • spec. D (parametri prototipi): dcl-parm (è opzionale: serve solo se il nome campo coincide con il nome di un codice operativo)
    • spec. D (costanti): dcl-c
    • spec. D (variabili stand-alone): dcl-s
    • spec. D (data structure): dcl-ds ... end-ds
    • spec. D (data structure – sottocampi): dcl-subf (è opzionale: serve solo se il nome campo coincide con il nome di un codice operativo)
    • spec. P (procedure): dcl-proc ... end-proc
  • per scrivere una specifica H vuota digitare solo ctl-opt;
  • alcune keyword devono essere scritte prima di altre. P.es. in una dichiarazione di DS (dcl-ds) la keyword ext deve precedere la keyword inz.
  • alcune keyword se non specificate vengono assunte per default. P.es. in una dichiarazione di file (dcl-f) viene assunto per default disk usage(*input).
  • la keyword usage consente di poter definire l’I/O permesso su un file con maggior precisione rispetto al formato fisso. P.es. un file può essere definito:
    • *input:*output: lettura e scrittura ma non aggiornamento
    • *delete: lettura e cancellazione
  • nella definizione del tipo dati il numero di decimali ‘0’ può essere omesso
  • se non è necessario specificare i nomi dei sottocampi di una ds bisogna specificare *n dove invece nel formato fisso si poteva semplicemente lasciare vuoto
  • è possibile utilizzare le costanti nella definizione di altre variabili. P.es.
dcl-c LenQta 13;
dcl-c DecQta 3;
dcl-s QtaOrdine packed(LenQta:DecQta);
  • ATTENZIONE: nel formato libero una stringa non racchiusa tra apici (unquoted) si riferisce sempre a un campo o a una variabile o a una costante. Quindi se per esempio si deve definire una DS basata su una DS esterna è sempre opportuno racchiudere tra apici il nome della DS esterna:

dcl-ds MiaStruttura extname('NOMEDS') end-ds;

  • Variabili “quasi” uguali. Se si ha la necessità di definire una variabile tramite la keyword like ma si desidera modificare la lunghezza si può scrivere così:

dcl-s TotQta like(QtaOrdine:+2);

  • Prototipi: se il nome del programma esterno coincide con quello della procedura si può omettere, in quanto il nome della procedura viene convertito in maiuscolo e usato come nome programma esterno

dcl-pr qcmdexc extpgm;

… equivale a

d qcmdexc pr extpgm('QCMDEXC')

  • Le definizioni in formato libero possono contenere porzioni di definizione condizionate dalle direttive di compilazione. P.es.
dcl-s TotImporto
/IF DEFINED(bignumber)
packed(17:2)
/ELSE
packed(13:2)
/ENDIF;

Tipi di dati per il formato libero

  • alfanumerico: char, varchar
  • UCS-2: ucs2, varucs2
  • graphic: graph, vargraph
  • indicatore: ind
  • packed: packed
  • zonato: zoned
  • binario: bindec
  • intero: int
  • intero senza segno: uns
  • a virgola mobile: float
  • data: date
  • tempo: time
  • timestamp: timestamp
  • puntatore: pointer
  • puntatore procedura: pointer(*proc)
  • oggetto: object

Con i tipi dati date o time è possibile specificare tra parentesi il formato che nel formato fisso viene specificato tramite le keyword datfmt/timfmt. P.es. date(*MDY-).

Nelle specifiche a formato fisso si definiva una variabile di tipo oggetto con la keyword CLASS, invece nel formato libero si usa direttamente il tipo dati OBJECT. P.es.

D obj S O CLASS(*JAVA:'MyClass')

equivale a

DCL-S obj OBJECT(*JAVA:'MyClass');

Formato totalmente libero (all-free or fully free)

  • disponibile da IBM i 7.2 TR3 o 7.1 TR11.
    PTF richieste per 7.2: SI58137 (RPG compiler, current target release), SI58110 (RPG compiler, previous target release), SF99702 liv. 9 (SQL precompiler).
    PTF richieste per 7.1: SI58136 (RPG compiler, current target release), SF99701 liv. 38 (SQL precompiler). Quindi in 7.1 non è supportata la compilazione su release precedenti.
  • è possibile scrivere codice dalla colonna 1 fino alla 240
  • per indicare al compilatore che si sta utilizzando il formato totalmente libero bisogna scrivere **FREE oppure **free a partire dalla prima colonna
  • dopo la keyword **FREE non è possibile scrivere codice a formato fisso
  • Con RPG fully free posso scrivere i membri sorgenti anche in file sorgenti con lunghezza superiore a 112

Espressioni

Alcuni esempi di espressioni comparate con i codici operativi a formato fisso:

  • somma:
    FIELD1 ADD FIELD2 FIELDR --> FIELDR = FIELD1 + FIELD2;
  • moltiplicazione:
    FIELD1 MULT FIELD2 FIELDR --> FIELDR = FIELD1 * FIELD2;
  • sottrazione:
    FIELD1 SUB FIELD2 FIELDR --> FIELDR = FIELD1 - FIELD2;
  • azzeramento e somma:
    Z-ADD FIELD2 FIELDR --> FIELDR = FIELD2;
  • azzeramento e sottrazione:
    Z-SUB FIELD2 FIELDR --> FIELDR = -(FIELD2);
  • divisione:
    FIELD1 DIV FIELD2 FIELDR --> FIELDR = FIELD1/FIELD2 (o bif %div)
    MVR FIELD3 --> %rem(FIELD1:FIELD2);

IMPORTANTE: le espressioni non troncano il risultato se il campo non è sufficientemente grande per contenerlo; bensì si riceve l’errore RNQ0103.

Definizione file

Nelle specifiche di definizione dei file (dcl-f) alcune keyword vengono assunte di default anche in relazione al tipo di file.

  • tipo di file: DISK
  • utilizzo file: per i tipi file DISK è *INPUT, per i tipi file PRINTER è *OUTPUT

Alternative al codice operativo MOVE

In free non esiste il codice operativo MOVE. Si può usare EVALR. Però bisogna porre attenzione che sovrascrive sempre il contenuto della variabile di destinazione.

From5      = 'ABCDE';
To10       = 'XXXXXXXXXX';
Evalr To10 = From5; //To10 will contain '     ABCDE’

To10       = From5; // To10 contains 'ABCDE     '
To10  = *all'X';
From5 = *all'A';
Evalr %subst(TO10:6) = From5;     //’XXXXXAAAAA’
%subst(To10:1:5)     = From5;     //’AAAAAXXXXX’

Troncamento

To5     =     *all'X';
From10  =     'ABCDEFGHIJ';
       To5 = From10; // to5 will be ‘ABCDE’
Evalr  To5 = From10; // to5 will be ‘FGHIJ’

Conversione da numero positivo a campo alfanumerico

MyNbr      = 12345.67;
To10       = *all'x';
      //to10 contains '   1234567‘
Evalr to10 = %editc(mynbr:'X');
      //to10 contains ‘0001234567‘
Evalr to10 = '0000000000' + %trim(%editc(myNbr:'X'));
      //to10 contains ‘0012345,67‘
Evalr to10 = '0000000000' + %trim(%editc(myNbr:‘3'));

con aggiunta di carattere di riempimento:

MyNbr1  = 1234567.89;
        // ‘$1234567,8’
 to10 = %editc(myNbr1:'3':'$');
        // ‘01234567,8’
 to10 = %editc(myNbr1:'3':'0');
 MyNbr= 123.89;
        // ‘**123,89’
 to10 = %editc(myNbr:'3':*astfill);

Sottostringare un campo numerico

MyNbr = 12345.67;
  // a destra: MyNbr2V0 = 67. !!! Non funziona con i negativi
MyNbr2V0 = %int(%subst(%editc(MyNbr:'X'):6:2));
MyNbr = -12345.67;
  // a destra:MyNbr2V0 = -67
MyNbr2v0 = %rem(%int(MyNbr*%int(10**%decPos(MyNbr))):100);
  // a sinistra: MyNbr2V0 = -12
MyNbr2V0 = %int(%subst(%editc(MyNbr:'L'):1:2));//12
EvalR Sign1 = %editc(MyNbr:'L');             // ‘-’
If Sign1='-';
  MyNbr2V0 *= -1;
Endif;

Alternative a MOVEA

Non esiste l’equivalente in formato libero del codice operativo MOVEA; è necessario modificare la definizione dell’array affinché esista una DS che abbia come sottocampo l’array. A questo punto dove nel formato fisso si usa la variabile Array con il codice operativo MOVEA, invece nel formato libero si usa la DS con il codice operativo EVAL.

Per es.

    dcl-ds dsArray;
       Array char(2) dim(10);
    end-ds;                                                                                                                                                                                      

    dcl-s tmp char(20);
    dcl-s i uns(3);
  
  // versione in formato fisso  
c     1             do        10            i 
c                   eval      Array(i) = %char(i) 
c                   enddo 
c                   movea     Array         tmp 
c                   movea(p)  'AaBbCcDdEeFf'Array 
  
  // versione in formato libero 
    for i = 1 to %elem(Array); 
     Array(i) = %char(i);  
    endfor;  
  
    tmp = dsArray; 
    dsArray = 'AaBbCcDdEeFf';  
  
    *inlr = *on;  

Un’altra alternativa è l’utilizzo della BIF %subarr (disponibile da V5R3). P.es.

%Subarr(Array:3:2) = 'AaBb';

Alternative al ciclo infinito DO *HIVAL

L’istruzione DO *HIVAL non ha una corrispondenza esatta nel formato libero.

Si può scrivere un costrutto do while con un condizione sempre vera.

dcl-s ForEverTrue inz('1') ind;
setll ...;
dow ForEverTrue;
 read/reade...;
 if %eof;
  leave;
 endif;
enddo;

Alternativa a KLIST

Invece di definire le chiavi composte usando il codice operativo KLIST si può definire una ds qualificata con i campi chiave e poi usarla con la funzione %kds.

Ipotizzando che il file XPROJAC1 abbia i campi chiave PROJNO, ACTNO, ACSTDATE.

Definisco la ds

dcl-ds KeyTest likerec(PROJACT:*key);

Valorizzo i campi chiave

KeyTest.PROJNO = 'AD3110';
KeyTest.ACTNO = 10;
KeyTest.ACSTDATE = '1982-01-01';

Leggo il file

chain %kds(KeyTest) XPROJAC1;

Tra l’altro la funzione %kds ha un parametro opzionale che consente di definire quanti campi chiave si desidera utilizzare con un notevole risparmio di definizioni di KLIST. P.es.

setll %kds(KeyTest) XPROJAC1;
do....;
  reade %kds(KeyTest:1) XPROJAC1;
  ...
enddo;

Si può usare anche un elenco di campi (o costanti) singoli come chiave, evitando quindi sia di definire una KLIST sia una ds per la funzione %kds, p.es.

chain (wProjNo:10:wAcstDate) XPROJAC1;

Alternativa alle schiere a tempo di compilazione

Di solito le schiere a tempo di compilazione vengono usate per scrivere le costanti di messaggi diagnostici da esporre all’utente. La relativa scomodità delle schiere a tempo di compilazione è che la definizione della schiera deve essere scritta nelle specifiche D (quindi all’inizio del sorgente) invece i valori vanno scritti in coda al sorgente. Se si definiscono più schiere a tempo di compilazione il risultato può essere confusionario o per lo meno scomodo.

Un buon metodo alternativo per scrivere tutto nelle specifiche D è definire una DS con un array che si sovrappone.

Il numero di elementi dell’array deve corrispondere al numero di messaggi

P.es.

   d Messaggi        ds
   d                               70a   inz('Messaggio numero 1')
   d                               70a   inz('Messaggio numero 2 molto lungo +
   d                                     anzi lunghissimo')
   d  Msg                          70a   overlay(Messaggi) dim(2)

* oppure in formato libero
dcl-ds Messaggi; *n char(70) inz('Messaggio numero 1'); *n char(70) inz('Messaggio numero 2 molto lungo anzi lunghissimo'); Msg char(70) pos(1) dim(2); end-ds;

Alternativa a CALL

Nel formato libero si può ottenere lo stesso risultato di un codice operativo CALL scritto in formato fisso definendo una procedura con la keyword extpgm.

P.es.:

dcl-pr MioPgm extpgm;
 parm1 char(10);
 parm2 char(2);
end-pr;
dcl-s campo1 char(10);
dcl-s campo2 char(2);
campo1 = 'valore_1';
clear campo2;
MioPgm(campo1:campo2);
...

Aree dati

Dichiarazione della DS Area1 per mappare il contentuto dell’area dati MIAAREA nella libreria QGPL.

dcl-ds Area1 dtaara('QGPL/MIAAREA');
 campo1 char(100);
 campo2 char(1) overlay(campo1:30);
end-ds;
...
in Area1;

Dichiarazione LDA

dcl-ds *n dtaara(*auto);
 SubField1 char(10) pos(50);
end-ds

Limitazioni

  • le specifiche I e O prevedono solo la sintassi a formato fisso
  • il ciclo RPG deve essere scritto in formato fisso
  • le schiere basate su tabelle (ovvero la keyword FROMFILE) non sono gestite nel formato libero delle specifiche D

Limitazioni con tool WebGate400

Nel caso si utilizzi il tool per la modernizzazione delle proprie applicazioni “WebGate400” si applicano le seguenti limitazioni:

  • le specifiche di definizione dei file video e delle ds infds dei file video devono essere scritte in formato fisso. Se sono presenti altre definizioni di file in formato libero è consigliabile porre le definizioni del file video in coda a tutte le altre.
  • è obbligatorio delimitare il codice in formato libero con le direttive /free.../end-free.
  • lunghezza massima nomi delle subroutine è 14 caratteri
  • dopo i codici operativi di I/O sui record dei file video non è possibile utilizzare le bif per intercettare l’esito dell’operazione (p.es. %eof, %found…) ma bisogna utilizzare gli indicatori

Bibliografia