GUIDA AL LINGUAGGIO ERRE (v 3.0)

ERRE LANGUAGE TUTORIAL (v 3.0)

 

Introduzione

I tipi e le variabili

Struttura di un modulo ERRE

Gestione delle eccezioni

Espressioni e funzioni predefinite

Direttive per il compilatore

Librerie di sistema

Versione per C-64

Gestione dei Tipi Astratti di Dati

 

Introduzione
 

Dal  punto di vista paradigmatico,  ERRE è  un linguaggio imperativo  di tipo procedurale, come si può vedere dallo schema sottostante.

 

ERRE, inoltre, è un linguaggio strutturato e fortemente non tipizzato: infatti i tipi disponibili sono sette e cioè:

1) "tipi semplici" : INTEGER, BOOLEAN, REAL, LONG REAL e STRING

2) "tipi aggregati": ARRAY e RECORD - che fanno riferimento ai cinque precedenti, si parlerà cioè di ARRAY di INTEGER, di BOOLEAN, di REAL, di LONG REAL e di STRING. Il tipo RECORD può fare riferimento sia a variabili semplici che con indice e può contenere anche dati di tipo diverso.

Quindi l'utente, contrariamente alla tendenza attualmente seguita, è molto limitato nella scelta dei tipi e deve limitarsi ad usare quelli predefiniti del linguaggio: questo fatto, se da un lato può essere limitativo per ciò che concerne l'uso di strutture dati complesse, dall'altro consente di ridurre la parte dichiarativa all'indispensabile.
E' inoltre possibile sviluppare una programmazione di tipo modulare in quanto l'utente può definire una gerarchia di moduli che si richiamano secondo certe modalità che verranno definite più avanti.
L'esposizione della sintassi del linguaggio seguirà il metodo top-down per la quale ciascun termine introdotto e non spiegato ad un certo livello sarà dettagliato a quello successivo.

 

I tipi e le variabili
 

Le variabili in ERRE non devono essere esplicitamente dichiarate, ma sono definite la prima volta che vengono usate: se non viene fornito loro un valore tramite un istruzione di assegnamento o di ingresso dati (INPUT o READ) sono inizializzate a zero tutte le variabili numeriche e a stringa vuota ("") tutte le variabili stringa; la dichiarazione di tipo è esplicita poiché ogni variabile viene dotata del proprio specificatore. Solo i tipi ARRAY e RECORD, in quanto tipi aggregati, vanno dichiarati usando l'apposita clausola DIM.

Ad esempio nel programma seguente:
 

               PROGRAM VARS

               DIM V%[10]
   -- istruzione di dimensionamento per variabili aggregate

               BEGIN
                     I=J+2          
 -- usate due variabili semplici di tipo REAL: J viene
                     PRINT(I,J) 
     creata all'atto della valutazione dell'espressione e
      
END PROGRAM        posta uguale a zero; poi viene creata anche I e
                                                   calcolata tramite l'istruzione di assegnazione

 

il risultato sarà

 2  0

perché le variabili semplici sono due (I e J) di tipo REAL; J è stata inizializzata a 0 e così è stato possibile calcolare anche I come 0+2. La variabile aggregata V% è un array di 11 interi (con indici compresi tra 0 e 10) che sono stati tutti inizializzati a zero.

In ogni caso, per motivi sia di chiarezza programmativa  che di efficienza di esecuzione,  è possibile dichiarare  le variabili semplici  usando  sempre la clausola DIM; l'esempio precedente può essere scritto come

               PROGRAM VARS2

               
DIM I,J         ◄-- dimensiona due variabili di tipo REAL: vengono entrambe inizializzate a 0
               DIM V%[10]   -- dimensiona la variabile intera aggregata V%
 
               BEGIN
                    
I=J+2        ◄-- I viene riassegnata e vale 2
                     PRINT(I,J) 
               END PROGRAM

Gli specificatori di tipo sono i seguenti:

             "%" : tipo INTEGER

             "$" : tipo STRING

             "#" : tipo LONG REAL (DOUBLE)

Le variabili di tipo REAL non dispongono di nessun specificatore mentre le variabili di tipo BOOLEAN possono essere sia segnate con "%" che non segnate.

-- INTEGER : le variabili di questo tipo sono associate a valori numerici interi e hanno un massimo e un minimo numero rappresentabile, precisamente una variabile di tipo INTEGER sarà compresa tra -MAXINT-1 e +MAXINT ove MAXINT è una costante riservata del linguaggio definita dall'implementazione.
Se si utilizzano solo variabili di questo tipo è possibile, usando l'apposita direttiva di compilazione, non indicare lo specificatore di tipo '%'.
Esempio:

            
INDEX%=-10

-- REAL : le variabili di questo tipo sono associate ai valori numerici decimali e, come nel caso precedente, hanno un massimo ed un minimo valore rappresentabile (precisamente sono comprese tra -MAXREAL e +MAXREAL) con una precisione fissa e dipendente dall'implementazione.
Esempio:

            
GRAVITY=9.81

-- LONG REAL : le variabili di questo tipo sono associate anch'esse ai valori numerici decimali e dispongono, rispetto al tipo REAL, di una precisione maggiore. Se si utilizzano solo variabili di questo tipo è possibile, usando l'apposita direttiva di compilazione, non indicare lo specificatore di tipo '#'.
Esempio:

               MAXIMUM#=139.8100017#

-- STRING : le variabili di questo tipo sono usate per rappresentare valori che consistono in una sequenza di caratteri definiti dal codice ASCII: il tipo STRING perciò consiste di zero o più caratteri racchiusi tra doppi apici.
Esempio:

           
TEXT$="This is a string"

-- BOOLEAN : un valore booleano è un valore logico di verità. ERRE mette a disposizione due costanti predefinite TRUE e FALSE a cui si associano convenzionalmente i valori ─1 e 0; ogni espressione possiede un valore booleano ed è perciò usabile con gli operatori logico-relazionali.

Esempio:

           
LOGICAL%=FALSE

-- ARRAY : il tipo ARRAY è usato per raggruppare oggetti (detti elementi) dello stesso tipo in una singola struttura dati; questi elementi sono manipolati tramite uno o più indici che ne individuano univocamente la posizione entro la struttura dati. Questo tipo richiede una dichiarazione esplicita (che riguarda le sue dimensioni).

-- RECORD : il tipo RECORD è usato per raggruppare oggetti anche di tipo diverso (detti "campi") in una singola struttura dati; possiamo avere sia record "semplici" che "aggregati" (manipolati tramite un indice) e richiedono una dichiarazione esplicita tramite lo statement TYPE che ne descrive la struttura.

Come si vedrà più avanti ERRE dispone di un certo numero di funzioni predefinite: dall'elenco di queste si vede che ne esistono solo due che permettono conversioni di tipo tra uno all'altro e precisamente la "STR$" che converte un tipo numerico in tipo stringa e la "VAL" che compie l'operazione inversa.
Questo si spiega col fatto che qualsiasi conversione tra i tipi INTEGER, REAL, LONG REAL e BOOLEAN (chiamati nel loro complesso "tipi numerici") viene compiuta automaticamente (conversione automatica di tipo).
Questa particolarità, se ben sfruttata, assicura al programmatore un uso molto ampio degli assegnamenti.

E' possibile, inoltre, allo scopo di migliorare la leggibilità del codice usare il carattere "_" all'interno di un identificatore (nomi di variabile o identificatori di procedure e function):

          
MAX_COUNT%=9999

Si presti attenzione al fatto che il carattere "_" non differenzia l'identificatore stesso: così, secondo il nostro ultimo esempio, le variabili "MAX_COUNT%" e "MAXCOUNT%" rappresentano lo stesso oggetto.


  Il tipo generico

Un'altra caratteristica di ERRE è la possibilità di utilizzare il cosiddetto tipo generico che, assieme alle procedure 'variant',consente di scrivere parti di codice che non dipendono dal tipo di variabili utilizzate come ad esempio l'ordinamento di un vettore.
Una variabile (semplice o aggregata) di tipo generico è riconoscibile perché utilizza '?' come specificatore di tipo.

 

Struttura di un modulo ERRE
 

Nella figura seguente è illustrata la struttura di un modulo ERRE: per chiarezza le parole chiave del linguaggio verranno scritte in maiuscolo. Inoltre la parti racchiuse tra parentesi graffe indicano ripetizione per zero o più volte.
I commenti possono essere inseriti ovunque nel testo e devono essere preceduti dal carattere "!" : un commento termina alla fine della linea.


            PROGRAM nome_prog

            { dichiarazione di variabili comuni, etichette, costanti, variabili
       semplici, array, record, funzioni e classi}

            { [VARIANT] PROCEDURE nome_proc(param_ingresso->param_uscita)
            { LOCAL lista_var_locali }
     
               ┌───────────────┐
     
                │  B l o c c o  │
 
                    └───────────────┘
             END PROCEDURE }
 

     { dichiarazione e corpo di eccezione }


            BEGIN
  
                   ┌───────────────┐
 
                    │  B l o c c o  │
 
                    └───────────────┘
            END PROGRAM

                                                  Fig. 1 : Struttura di un modulo ERRE
 


  Dichiarazione di etichetta

Una dichiarazione di etichetta è:

         
LABEL n1,n2,.....,nj

dove "n1","n2",....,"nj" sono numeri interi senza segno e servono per identificare dei punti di salto, utilizzati tramite l'istruzione GOTO. Per esempio la dichiarazione

         
LABEL 10,100,999

definisce tre punti di salto identificati con "10:","100:","999:".


  Dichiarazione di costante

Una dichiarazione di costante è:

           CONST c1=v1,c2=v2,........,cj=vj

dove "c1","c2",....,"cj" sono identificatori e v1,v2,....,vj i rispettivi valori espressi come letterali. Per esempio la dichiarazione

           CONST ALFA=100,PLUTO$="XXXXX"

definisce la costante numerica ALFA come 100 e la costante stringa PIPPO$ come "XXXXX"


•  Dichiarazione di variabili comuni

Una dichiarazione di variabili comuni è:

         
COMMON var1,var2,......,varj

sono var1,var2,...,varj costituiscono una lista di variabili (semplici o aggregate) che possono essere passate ad un altro programma richiamato con l'istruzione CHAIN. Le variabili vanno indicate con il proprio indicatore di tie quelle aggregate vanno seguite anche da "[]". Le variabili dell'istruzione COMMON devono corrispondere in tipo (ma non necessariamente nel nome) sia nel nel programma chiamante che in quello chiamato.
Questa dichiarazione è tipicamente usata in una gestione a moduli di un programma ERRE.


•  Dichiarazione di tipo

Una dichiarazione di tipo è

         
TYPE nometipo IS (lista di campi)

dove "nometipo" è il nome del tipo cui faranno riferimento le variabili di tipo record che verranno dichiarate tramite lo statement "DIM" e "lista di campi" è la lista dei campi che compongono il record, ognuna con il proprio specificatore di tipo, separati da ",". Per esempio la dichiarazione

            TYPE CUSTOMER IS (NOME$,INDIRIZZO$,NUMTEL$)

definisce il tipo "CUSTOMER" composto da tre campi stringa ("NOME$","INDIRIZZO$" e "NUMTEL$"). Tramite un'istruzione DIM successiva ci si potrà riferire a "CUSTOMER"

             DIM WORK:CUSTOMER,ELENCO[10]:CUSTOMER

creando così il record semplice WORK e l'array ELENCO composto di undici record, entrambi di tipo CUSTOMER.

Nel corso dell'esecuzione si farà riferimento a queste variabili scrivendo, ad esempio:

             WORK.INDIRIZZO$
oppure
             ELENCO.INDIRIZZO$[5]

Si noti che, nella DIM, non si utilizza lo specificatore di tipo poiché questi è già contenuto a livello del singolo campo.


  Dichiarazione di array

Una dichiarazione di array è :

             DIM a1[d1,...,dN1],a2[d1,...,dN2],.......,aj[d1,...,dNj]

dove "a1","a2",..."aj" sono delle variabili di tipo ARRAY, "N1", "N2",..,"Nj" rappresentano il numero delle dimensioni di ogni array dichiarato; "d1","d2", ...,"dn" sono gli estremi superiori degli indici degli array in quanto l' estremo inferiore parte obbligatoriamente da zero: perciò gli indici indirizzabili vanno da 0 a dj. Per esempio la dichiarazione :

             DIM A$[10],B%[5],F2[10,10]

dimensiona il vettore di stringhe A (con 11 elementi con indice da 0 a 10),il vettore di interi B (con 6 elementi) e la matrice di reali F2 (con 121 elementi, da F2(0,0) a F2(10,10)).


  Dichiarazione e chiamata di funzione

Una dichiarazione di funzione è :

               FUNCTION nome_funzione(var1,var2,...,varN)
                       nome_funzione=espressione
               END FUNCTION


dove "var1","var2",...,"varN" sono le variabili locali della funzione ed "espressione" è una espressione. Un esempio di funzione è:

               FUNCTION PARABOLA(X)
                     PARABOLA=X*X+5*X+6
               END FUNCTION


in questo caso "var1" è X ed "espressione" è X*X+5*X+6.
Una funzione restituisce, al contrario di una procedure, un solo valore ed il suo tipo deve essere specificato nel "nome_funzione" esattamente come nell'identificatore di una variabile: la funzione dell'esempio è di tipo REAL.

Una chiamata di funzione si effettua dando il nome seguito da una lista di espressioni, separate tra di loro con virgole, racchiusa tra una coppia di parentesi rotonde: una chiamata della funzione "PARABOLA" può essere il seguente:

               ZETA=X+Y+PARABOLA(X+Y)


•  Dichiarazione e chiamata di procedure

La parola chiave PROCEDURE è seguita da un identificatore e, facoltativamente da una lista di parametri formali, separati tra di loro con virgole, e racchiusa con una coppia di parentesi rotonde. Un esempio di procedure è:

               PROCEDURE FACTORIAL(X%->F)
                     F=1
                     IF X%<>0 THEN
                          FOR I%=X% TO 2 STEP -1 DO
                               F=F*X%
                          END FOR
                     END IF
               END PROCEDURE


Il separatore "->" distingue i parametri di ingresso da quelli di uscita: nel nostro caso "X%" e il parametro di ingresso e "F" quello di uscita.

Come parametri formali è possibile indicare anche array e funzioni, che essendo "formali" non vanno dichiarate.

E' possibile dare, tramite la parola chiave LOCAL, una lista di variabili cosidette "locali" che possono essere usate all'interno della procedura e che vengono azzerate ad ogni richiamo della procedura stessa: le variabili globali con lo stesso nome non vengono, ovviamente, influenzate dall'utilizzo di quelle locali all'interno della procedura stessa.

L'ultimo esempio si poteva scrivere meglio come:

                PROCEDURE FACTORIAL(X%->F)
                     LOCAL I%
                     F=1
                     IF X%<>0 THEN
                            FOR I%=X% TO 2 STEP -1 DO
                                 F=F*X%
                            END FOR
                     END IF
                END PROCEDURE


cosicché una eventuale variabile "globale" I% non viene alterata, in valore, dalla I% "locale".

Una chiamata di procedure si effettua tramite il nome seguito da una lista di parametri attuali racchiusi in una coppia di parentesi rotonde; nel nostro caso :

                FACTORIAL(N%->FACT)

cosicché all'atto della chiamata N% viene assegnato ad X%, mentre all'uscita il risultato F viene assegnato a FACT: tra parametri formali e attuali deve esistere perciò una corrispondenza in numero e tipo. Sia le procedure che le funzioni consentono l'uso di parametri per passare informazioni da chiamante a chiamata: la sola differenza consiste nel fatto che le funzioni restituiscono sempre un valore, cosicché è possibile chiamare una funzione, ma non una procedure, nell'ambito di una espressione.

Possono essere passati come parametri anche array, record e funzioni: per gli array basta specificare dopo il nome il simbolo "[]", per i record basta indicare la variabile seguita da '.', mentre per le funzioni (predefinite o dichiarate dall'utente), basta specificare il nome seguito dalla lista di parametri.

Ad esempio, è possibile dichiarare la

             
PROCEDURE CALCOLA(A#,B#,DELTA#,DUMMY#(X))

dove DUMMY è la funzione formale con i suoi parametri che NON viene dichiara-
ta; un possibile richiamo è il seguente:

             
CALCOLA(0,10,0.1,SQR(X))

dove SQR prende il posto di DUMMY all'interno della procedura.

Oppure se dichiaro un record come

                TYPE MYREC=(ALFA,BETA,GAMMA)
                DIM PIPPO:MYREC,PLUTO[10]:MYREC


e una procedura

             
PROCEDURE ESEMPIO(I%,PLUTO.[]->ZETA$)

un possibile richiamo sarà:

             
ESEMPIO(I%,PLUTO.[]->ZETA$)


  La clausola FORWARD

Una procedura può essere dichiarata tramite la clausola FORWARD specificando separatamente l'intestazione dal blocco che rappresenta il corpo della procedura. L'intestazione è uguale a quelle viste nel paragrafo precedente eccettuato il fatto che viene chiusa dalla parola riservata FORWARD.
Il blocco, invece, segue più avanti e viene iniziato specificando solo il nome della procedura. Come esempio vediamo il programma seguente:

                PROGRAM CATCH22

                FUNCTION ODD(X%)
                      ODD=FRC(X%/2)<>0
                END FUNCTION

                PROCEDURE UP(I%─>I%)
! dichiarazione FORWARD di UP ....
                FORWARD

                PROCEDURE DOWN(I%─>I%)
                    I%=I% DIV 2
                    PRINT(I%)
                    IF I%<>1
                      THEN
                            UP(I%─>I%)           
! che permette questa chiamata ....
                     END IF
                END PROCEDURE

                PROCEDURE UP                
! dichiarazione del blocco della UP
                     WHILE ODD(I%) DO
                          I%=I%*3+1
                          PRINT(I%)
                     END WHILE
                     DOWN(I%─>I%)           
! chiama la DOWN che a sua volta la
                                                             ! richiama.
                END PROCEDURE

                BEGIN
                     PRINT("Imposta un numero intero ....";)
                     INPUT(X%)
                     UP(X%─>X%)
                     PRINT("Ok, il programma si è fermato di nuovo.")
                END PROGRAM


In questo caso la procedura UP viene dichiarata usando la clausola FORWARD per poter accedervi all'interno della procedura DOWN: in questo modo si realizza la mutua ricorsione tra due procedure.
Il blocco della procedura UP viene invece dichiarato più avanti e viene intestato con il nome della procedura senza alcuna specifica.


•  Le 'variant procedure'

Come detto nel par. 2.1.1 è possibile scrivere parti di codice indipendenti dal tipo di variabile usata: questo si ottiene utilizzando il tipo 'generico' e le procedure 'variant'. Supponiamo, ad esempio, di voler scrivere una routine di ordinamento che tratti sia dati numerici che alfabetici; normalmente si scriveranno due procedure distinte del tipo:

                DIM A[100],A$[100]

                PROCEDURE NUM_SORT(A[],N%)
                    LOCAL I%,FLIPS%
                    FLIPS%=TRUE
                    WHILE FLIPS% DO
                         FLIPS%=FALSE
                         FOR I%=1 TO N%-1 DO
                              IF A[I%]>A[I%+1] THEN
                                   SWAP(A[I%],A[I%+1])
                                   FLIPS%=TRUE
                              END IF
                          END FOR
                    END WHILE
                END PROCEDURE

                PROCEDURE ALFA_SORT(A$[],N%)
                    LOCAL I%,FLIPS%
                    FLIPS%=TRUE
                    WHILE FLIPS% DO
                        FLIPS%=FALSE
                        FOR I%=1 TO N%-1 DO
                             IF A$[I%]>A$[I%+1] THEN
                                   SWAP(A$[I%],A$[I%+1])
                                   FLIPS%=TRUE
                             END IF
                        END FOR
                     END WHILE
                END PROCEDURE


con le relative chiamate

                NUM_SORT(A[],50) o ALFA_SORT(A$[],100)

Come si può vedere il codice (eccetto per il tipo dell'array A) è esattamente lo stesso:qui, allora, subentrano il tipo generico e le procedure variant.
E' possibile unificare le due routine in una sola dichiarando sia l'array A come generico (con '?') sia come 'variant' la procedure SORT:

                DIM A?[100]

                VARIANT PROCEDURE SORT(A?[],N%)
                    LOCAL I%,FLIPS%
                    FLIPS%=TRUE
                    WHILE FLIPS% DO
                        FLIPS%=FALSE
                        FOR I%=1 TO N%-1 DO
                            IF A?[I%]>A?[I%+1] THEN
                                 SWAP(A?[I%],A?[I%+1])
                                 FLIPS%=TRUE
                            END IF
                        END FOR
                    END WHILE
                END PROCEDURE


e le relative chiamate diventeranno

                SORT(A[],50) 

                                               oppure          

                SORT$(A$[],100)

Si noti che dopo il nome della procedura ne viene fornito anche il tipo come come fosse una variabile 'normale' e che si possono utilizzare accanto alle variabili 'generiche' anche quelle 'non generiche'.

Comunque mentre una procedura normale può chiamare una di tipo variant, una 'variant' non può chiamare un'altra 'variant'

E' disponibile, inoltre, la costante variant EMPTY che consente di azzerare una variabile 'variant' (semplice o elemento di array): il suo uso è consentito solo all'interno di procedure di tipo variant.


 Scopo di un oggetto

Per "scopo di un oggetto" intendiamo le regole che disciplinano l'ambito della sua validità.
La gerarchia delle procedure è stabilita dall'ordine delle dichiarazioni: tale ordine è alterabile solo ricorrendo alla clausola FORWARD.
Il programma principale (main) può chiamare qualsiasi procedure perché può essere considerato, in un certo senso, l'ultima procedure dichiarata, salvo il fatto che specifica l'inizio dell'esecuzione di un programma.
Le funzioni sono accessibili da qualsiasi punto del programma: anche qui vale però la gerarchia stabilita dall'ordine delle dichiarazioni. Per esempio se nell'ordine vengono dichiarate le funzioni F1,F2 e F3 e le procedure P1 e P2 allora

               F1 può essere chiamata da F2,F3,P1,P2 e dal main
               F2 può essere chiamata da F3,P1,P2 e dal main
               F3 può essere chiamata da P1,P2 e dal main
               P1 può essere chiamata da P2 e dal main
              
P2 può essere chiamata solo dal main.

Per ciò che concerne le variabili gli array dichiarati con DIM sono accessibili da qualsiasi punto del modulo, mentre le variabili usate come parametri formali nelle function sono da considerarsi locali. Qualsiasi altra variabile usata o come parametro formale in una dichiarazione di procedure o assegnata è da considerarsi globale: è possibile, però, tramite la clausola LOCAL, dichiarare variabili locali entro una procedura: come detto le variabili globali con lo stesso nome non vengono così influenzate.

Consideriamo il seguente esempio:

                PROGRAM ESEMPIO

                DIM A%[20]
       ◄---- A% è globale

                FUNCTION CUBO(X)
 ◄---- X è locale in CUBO
                     CUBO=X*X*X
                END FUNCTION

                PROCEDURE STAMPA(A%[],Z%)
◄---- Z% è globale
                     LOCAL I%                                
◄---- I% è locale in STAMPA e diversa da
                     FOR I%=1 TO Z% DO                            
quella utilizzata nel main
                           PRINT(A%[I%])
                     END FOR
                END PROCEDURE

                BEGIN
                    X=-45
          ◄----- X,I% sono globali e diverse da quelle
                    FOR I%=1 TO 20 DO            
locali viste precedentemente
                         A%[I%]=CUBO(I%)
                    END FOR

                    I%=99
                    STAMPA(A%[], 20)
                    PRINT(X,I%,Z%)
◄----- otterrò come risultato
                END PROGRAM
         -45 99 20
                             Z% è stata ricavata dal passaggio di
                             
parametri nella riga precedente.


•  Gli statement

Il termine "Blocco" della fig. 1 indica l'insieme dei seguenti statement:

-- FOR var=espressione TO espressione STEP espressione DO
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   END FOR


Il ciclo sarà ripetuto per tutto l'insieme di valori di "var" specificati: se questo insieme fosse vuoto il corpo del ciclo non sarà eseguito. La parola chiave STEP è facoltativa se il passo del ciclo è unitario.
Ad esempio per stampare una tabella dei quadrati dei numeri pari da 1 a 100 si scrive:

            FOR NUMERO%=2 TO 100 STEP 2 DO ! il passo del ciclo è 2
                 PRINT(NUMERO%,NUMERO%^2)
            END FOR


-- FOREACH var IN (lista) DO
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   END FOR


Il ciclo sarà ripetuto per tutto l'insieme di valori di "var" specificati in (lista): (lista) NON può essere vuota. Ad esempio, il seguente frammento di programma

            FOREACH I% IN (2,3,-1,0,4) DO ! la lista contiene 5 elementi
                  PRINT(I%,I%^2)
            END FOR


stamperà

 2 4
 3 9
-1 1
 0 0
 4 16


Contrariamente al ciclo FOR "classico" in questo caso "var" può essere anche di tipo STRING; si potrà perciò scrivere ad esempio

            FOREACH I$ IN ("PIPPO","PLUTO","PAPERINO") DO
                 PRINT("Adesso guardo -> ";I$)
            END FOR


ottenendo come risultato:

Adesso guardo -> PIPPO
Adesso guardo -> PLUTO
Adesso guardo -> PAPERINO

 

-- REPEAT
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   UNTIL espressione


Il ciclo viene ripetuto finché l'espressione che segue UNTIL resta FALSE. Ad esempio, per calcolare l'espressione 1 + 1/2 + 1/3 + 1/4 +... finché la somma non superi un certo limite LIM prefissato, si scrive:

            SOMMA=0
            NUM%=0
            REPEAT
                NUM%=NUM%+1
                SOMMA=SOMMA+1/NUM% ! conversione automatica di tipo
            UNTIL SOMMA>LIM


-- WHILE espressione DO
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   END WHILE


Il ciclo viene ripetuto finché l'espressione che segue WHILE resta TRUE. Per esempio se vogliamo eliminare il fattore 2 dal valore di NUM% si scrive:

            WHILE NOT ODD(NUM%) DO
                NUM%=NUM%/2
            END WHILE


supponendo che ODD sia una funzione di tipo BOOLEAN che fornisce TRUE se l'argomento è un numero intero dispari.

-- LOOP
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   END LOOP


"Blocco" viene eseguita indefinitamente ("ciclo infinito"), quindi ci deve essere almeno una condizione di uscita, realizzata con una "EXIT". Ad esempio:

            I=0
            LOOP
                I=I+1
                PRINT(I;)
                EXIT IF I>5
            END LOOP


stamperà

 
1 2 3 4 5 6

uscendo quindi dal ciclo. Senza "EXIT" la stampa sarebbe proseguita indefinitamente e l'unica possibilità di fermare il programma è di usare una opportuna combinazione di tasti messa a disposizione dall'interprete di R-Code ospite.

-- IF espressione
    THEN
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   {
    ELSIF espressione
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘

   }
   [ELSE
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘]

   END IF


Questo statement permette di effettuare una scelta tra due alternative (o più se viene utilizzata la clausola ELSIF): se il valore di "espressione" è TRUE viene eseguito il blocco che segue THEN, altrimenti i vari rami ELSIF (se sono usati) ognuna con la propria "espressione" e se, alla fine, tutti questi valori sono FALSE viene eseguito quello che segue ELSE; quest'ultimo può anche essere facoltativo. Ad esempio, per vedere se un carattere assegnato è una lettera si scrive:

                IF A$>="A" AND A$<="Z")
                  THEN
                      PRINT("E' una lettera maiuscola!!")
                   ELSE
                      PRINT("Non è una lettera maiuscola!!")
                END IF


Per calcolare, invece, il numero dei giorni di un mese (M%) assegnato un anno qualsiasi (A%) si scriver…:

                IF M%=4 OR M%=6 OR M%=9 THEN
                     GG%=30
                  ELSIF M%=2 AND BISEST% THEN
◄--- BISEST% è un flag che indica se  l'anno A% è

                                           bisestile
                     GG%=29                                                   
                  ELSIF M%=2 AND NOT BISEST% THEN
                     GG%=28
                  ELSE
                      GG%=31
                END IF

 

Nota:  E' possibile, nel caso che "Blocco" sia composto da una sola istruzione e non sia presente ELSIF, omettere "END IF"  e scrivere tutto su una sola riga: un test del tipo

                 IF A=3 THEN
               
     B+=1
               
    ELSE
               
      B-=1
               
END IF

può essere scritto come

                 IF A=3 THEN B+=1 ELSE B-=1

su una sola riga.


-- CASE espressione OF
   { label[,label]─>
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘
   END ─> }
   [OTHERWISE
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘]
   END CASE


Questo statement permette di effettuare una scelta tra più alternative.
Il termine "label" si riferisce ad una espressione che sarà confrontata con l'"expression" che segue CASE. Più "label" possono essere messe sulla stessa linea separate da una virgola se "Blocco" è uguale per tutte; la parola chiave OTHERWISE è facoltativa e serve per gestire esplicitamente i casi non previsti dalle varie "label". Per esempio per tradurre una cifra araba NUM% in cifre romane, si scrive:

                CASE NUM% OF
                    1-> PRINT("I";) END ->
                    2-> PRINT("II";) END ->
                    3-> PRINT("III";) END ->
                    4-> PRINT("IV";) END ->
                    5-> PRINT("V";) END ->
                    6-> PRINT("VI";) END ->
                    7-> PRINT("VII";) END ->
                    8-> PRINT("VIII";) END ->
                    9-> PRINT("IX";) END ->
                    OTHERWISE
                         PRINT("Lo zero non è lecito.")
                END CASE


Riferendosi all'esempio precedente relativo all'istruzione IF..ELSIF, si poteva anche usare CASE:

                CASE M% OF
                     4,6,9->
                         GG%=30
                     END ->
                     2->
                         GG%=28
                         IF BISEST% THEN GG%=29 END IF
                     END ->
                    OTHERWISE
                         GG%=31
                END CASE

 

E' possibile  inoltre  specificare, come label, anche degli insiemi sia numerici interi che di caratteri, usando la notazione "..".


Vediamo alcuni esempi:

 Forma "standard"       Forma "alternativa"
-------------------------------------------------
  1,2,3,4,5->                1..5->
  "A","B","C","D","E"     "A".."E"->

E' pure possibile usare operatori relazionali,tramite la clausola IF, come nel seguente esempio:

   PROGRAM CASE_ESTESO
   BEGIN
     INPUT(N)
     CASE N OF
        IS >=10-> PRINT("MAGGIORE O UGUALE A 10") END ->
        IS <5-> PRINT("MINORE DI 5") END ->
      OTHERWISE
        PRINT("OUT OF RANGE")
     END CASE
   END PROGRAM

Introducendo un numero N, la prima label viene usata se N>=10, la seconda se N<5 e la terza in tutti gli altri casi. Si noti che non sarebbe stato possibile altrimenti usare un'istruzione CASE con il formato delle label in forma,  per così dire, "standard"  ed avremmo dovuto ricorrere ad un'istruzione IF..ELSIF.

-- GOTO etichetta
    .
    .
  etichetta
:


Consente di deviare il flusso dell'esecuzione verso un punto specifico del programma identificato da "etichetta".
Il salto viene eseguito a "etichetta" che è un numero intero senza segno compreso tra 1 e 9999 e seguito dal carattere ":". Mentre il punto identificato da "etichetta:" è unico, esso può essere raggiunto da più istruzioni GOTO. Ad esempio

               INPUT("NUMERO",I)
               IF I>100 THEN
                     A$="SUBITO"
                    GOTO 99
◄---- salto
               END IF
               PRINT("NUMERO <=100 !")
               A$="DOPO"
               99:
       ◄---- etichetta
               PRINT("ETICHETTA RAGGIUNTA ";A$;"!")


E' possibile effettuare salti solo all'interno di una stessa procedure o del main; qualsiasi salto tra procedure o tra procedure e main verrà evidenziato come errore: l'utilizzo di questo statement andrebbe comunque limitato anche se evidenzia la propria utilità nella gestione di particolari strutture di controllo.

-- WITH var.record DO
      ┌───────────────┐
      │  B l o c c o  │
      └───────────────┘
   END WITH


Consente, nel blocco di istruzioni che segue, di indicare un variabile di tipo record "var_record" solo con il nome del campo preceduto da ".", chiarendo meglio l'utilizzo dei record. Ad esempio si potrà scrivere

                WITH CUSTOMER DO
                     PRINT(.NOME$,.INDIRIZZO$,.CITTA$)
                END WITH


anziché

                PRINT(CUSTOMER.NOME$,CUSTOMER.INDIRIZZO$,CUSTOMER.CITTA$)

 

--  var=espressione

 

è lo statement generico di assegnamento: "var" è una variabile semplice e deve essere di tipo compatibile  con "expression" (cfr. § 2.1). In certi casi è possibile "abbreviare" questa istruzione usando gli alias (cfr. § 2.2.12).

Un esempio è

 

     VALUE=X+Y%-2^(1/MATRIX[I%,J%])

 

Altri due casi speciali riguardano le variabili aggregate: è possibile scrivere

 

    var_array=var_array1

    var_array=(lista)

 

con "var_array", "var_array1" e "lista" che appartengono a tipi tra loro compatibili (cfr. § 2.1). Ad esempio:

 

     MAT1[]=MAT2[]

     VET[]=(0,1,2,3,4,5,6,7,8,9)

 

La prima assegna direttamente  la matrice MAT2 alla matrice MAT1 (precedentemente dichiarate), senza utilizzare cicli FOR..END FOR.

La seconda, invece, permette di inizializzare un vettore o una matrice  sempre senza utilizzare istruzioni DATA..READ e un ciclo FOR..END FOR: gli indici si considerano consecutivi e a partire da zero.

 

Infine  è possibile, inoltre, in certi casi particolari scrivere in forma abbreviata una istruzione di assegnamento e precisamente:

 

● usando la cosidetta "inline if" - IIF: ad esempio

 

     RES$=IIF(N>=0,"Positivo","Negativo")

       ▲       ▲    ▲          ▲

       │       │    │          └─ usata se condizione è FALSE

   variabile   │    └─ usata se condizione è TRUE

   assegnam.   └─ condizione

 

che è equivalente a

 

    IF N>=0 THEN

        RES$="Positivo"

      ELSE

        RES$="Negativo"

     END IF

 

Nota: Dalla versione 3.0 è possibile utilizzare  IIF  (limitata alle espressioni numeriche) nella definizione di una funzione. Ad esempio è possibile scrivere una funzione MAX come segue:

      FUNCTION MAX(X,Y)
           MAX=IIF(X>=Y,X,Y)
      END FUNCTION 

● usando un'istruzione CHOOSE: ad esempio

 

     Y=CHOOSE(X,2,4,6,8,10)

     ▲        ▲ ▲ ▲ ▲    ▲

     │        │ │ │ │    │ usate se la variabile di test vale rispettivamen-

   variabile  │ └─┴─┴─...┘ te 1,2,3,...,N. Altrimenti non c'è assegnamento.

   assegnam.  └─ variabile

                 di test

 

che è equivalente a

 

     CASE X OF

       1-> Y=2 END ->

       2-> Y=4 END ->

       3-> Y=6 END ->

       4-> Y=8 END ->

       5-> Y=10 END ->

       OTHERWISE

         !$NULL

     END CASE

 

● usando un'istruzione SWITCH: ad esempio

 

     MAX=SWITCH(A>=B,A,A<B,B)

     ▲          ▲    ▲  ▲  ▲

     │          │    │  │  │

   variabile    │    │  └──┴  coppie formate da "condizione,valore": la prima

   assegnam.    └────┴─ condizione che è TRUE assegna il proprio valore alla

                        variabile di assegnamento. Se nessuna è TRUE non c'è

                        assegnamento.

 

   che è equivalente a

 

     IF A>=B THEN

         MAX=A

       ELSIF A<B THEN

         MAX=B

       ELSE

         !$NULL

     END IF


-- Gli statement di ingresso/uscita sono i seguenti:

◊ PRINT(lista_espressioni)

Stampa sul dispositivo di uscita "lista_espressioni" secondo la formattazione indicata. Questa può venire assegnata tramite le funzioni di tabulazione SPC e TAB o tramite i separatori ";" e "," che indicano rispettivamente stampa di seguito e stampa nel prossimo campo: la lunghezza di ogni campo dipende dalla larghezza dello schermo (40 o 80 colonne a disposizione).
Tutte le variabili di tipo INTEGER, BOOLEAN e REAL occupano un campo, le variabili di tipo LONG REAL possono occupare due campi, mentre per le variabili di tipo STRING conta la lunghezza delle stringhe stesse.

◊ WRITE(formato;lista_espressioni)

Stampa sul dispositivo di uscita "lista_espressioni" secondo la formattazione indicata dal "formato" (costante o variabile di tipo STRING). I formati possibili sono i seguenti:

ú numerici: si indica con '#' una cifra da stampare, con '.' la posizione del punto decimale e con '^' l'eventuale esponente.
Ad esempio se A=5.443E-1, allora

                WRITE("##.####^^^^";A) stamperà       5.4430E-01

ú stringa : si indica con "\" + (n-2) spazi + "\" la lunghezza della stringa da stampare.
Ad esempio se A$="PIPPOPLUTO", allora

                WRITE("\     \";A$)              stamperà       PIPPOP
                 ▲

                 │

              4 spazi


◊  INPUT({LINE,|"prompt",}lista_var)

Acquisisce valori da tastiera separandoli, eventualmente, da ",", e li attribuisce a "lista_var". Sono ammesse anche le due forme:


       ° INPUT(LINE,var) che legge caratteri da tastiera (incluso il carattere ",") fino al tasto
<Return> e li attribuisce alla variabile stringa "var".


       ° INPUT("prompt",lista_var) che stampa anche un prompt di richiesta delle variabili. Sostituisce le istruzioni:
        
                                                 PRINT("prompt";) INPUT(lista_var)


◊ GET(lista_var)

Acquisisce caratteri da tastiera e li attribuisce a "lista_var".

◊ READ(lista_var)

Legge valori da righe di DATA e li attribuisce a "lista_var".

◊ DATA(lista_costanti)

Contiene i valori letti da READ. E' possibile posizionare linee DATA sia nelle procedure che nel main del programma.

◊ RESTORE

Riposiziona il puntatore al primo dato scritto in una linea DATA. Se RESTORE è usata all'interno di una procedura, il puntatore è posizionato alla prima riga DATA all'interno della procedura stessa, altrimenti alla prima riga DATA del main.

               PROGRAM TEST_RESTORE

               PROCEDURE P1
                   LOCAL I%,A%
                   RESTORE      
◄---
posiziona il puntatore al primo DATA all'interno della
                                            
   
procedure *corrente* (cioè sul dato "5")
                   DATA(5,6,7,8)
                   FOR I%=1 TO 4 DO
                       READ(A%)
                       PRINT(A%;)
                    END FOR
                    PRINT
               END PROCEDURE

               BEGIN
                      FLAG%=FALSE
                      P1             
 ◄---
P1 riposizionerà il proprio puntatore
                      RESTORE 
◄---
posiziona il puntatore al primo DATA all'interno del
                        
main (dato "1")
                      DATA(1,2,3,4)
                      FOR I%=1 TO 4 DO
                            READ(A%)
                            PRINT(A%;)
                      END FOR
                      PRINT
                      P1            
◄---
P1 riposizionerà di nuovo il proprio puntatore
                      PRINT(FLAG%)
                      RESTORE
◄---
riporta il puntatore sul dato "1" del main
               END PROGRAM


La stampa ottenuta sarà la seguente:

  5 6 7 8
  1 2 3 4
  5 6 7 8


Dopo l'ultimo RESTORE, una READ ulteriore rileggerebbe il dato "1" del main.

◊ OPEN(tipo_file,numero_file,nome_file,[[LEN=]lung_record])
   OPEN("COMx:dati_porta_seriale",numero_file)


Apre un file
utilizzando i parametri indicati tra parentesi.

"tipo_file": il file può essere sequenziale ("I" per ingresso-input, "O" per uscita-output, "A" per accodare in scrittura-append) o ad accesso diretto ("R"-random). Solo in questo caso, nella dichiarazione necessita la specifica della lunghezza del record. "lung_record": l'identificatore LEN= è facoltativo e serve per migliorare la leggibilità.
"numero_file": è il numero del buffer  utilizzato dal file e dipende strettamente dalla piattaforma (nel caso del PC varia tra 1 e 255).

"nome_file": indica  il nome attribuito al file  e dipende strettamente dalla piattaforma;  nel caso del PC  può essere un file su disco (il cui nome segue quindi la sintassi DOS/Windows),  un dispositivo quale lo schermo "SCRN:", la tastiera "KYBD:", una porta stampante "LPTx:" con x che vale 1, 2 o 3  oppure una porta seriale "COMx:".  In questo caso si utilizza la seconda forma della istruzione OPEN (x può essere 1 o 2) e con "dati_porta_seriale" si indica  la velocità, il tipo di parità, il numero di bit di dati ed il numero di bit di stop: un esempio possibile è "2400,N,8,1".


L'apertura di un file può avvenire ovunque nel testo del modulo poiché i file sono da considerarsi oggetti globali.

◊ FIELD(lista)

Specifica la struttura di un file ad accesso diretto: il primo elemento della lista indica il numero di file, gli altri elementi, a coppie, rispettivamente la lunghezza e il nome di campo (variabile di tipo STRING). Ad esempio, se abbiamo una struttura relativa ad un archivio CLIENTI del tipo
 

                              ┌─────────────┬───────────┬──────────────┬───────┐
                              │ RAGIONE SOC.│ INDIRIZZO │ CITTA E PROV.│  CAP  │
                              └─────────────┴───────────┴──────────────┴───────┘
                                  CLRSOC$      CLINDIRI$     CLCITTA$   CLCAP$
                                    L=27        L=27           L=23       L=5
 

possiamo dichiarare un file ad accesso diretto "CLIENTI.DAT" con lunghezza di record pari a 82 caratteri e composto di 4 campi in questo modo

               .......
               OPEN("R",1,"CLIENTI.DAT",LEN=82)
               FIELD(#1,27,CLRSOC$,27,CLINDIRI$,23,CLCITTA$,5,CLCAP$)
               .......


Si noti che la somma delle lunghezze dei campi dell'istruzione "FIELD" non deve superare quella dichiarata nella "OPEN". E' possibile anche aprire un file ad accesso diretto che usa un record dichiarato con la clausola "TYPE" che, come si può notare, presenta parecchie analogie con "FIELD". Ad esempio, se si ha

               TYPE DIPENDENTE=(NOME$,MATR$,ORARIA%,SALARIO#)

si può dichiarare un file ad accesso diretto basato su questa struttura record come

               OPEN("R",1,"PAYROLL.DAT",^DIPENDENTE) ◄--- '^' indica puntatore al record DIPENDENTE
               FIELD(#1,27,11,2,8)
◄--- nella FIELD basta indicare solo le lunghezze dei campi
                       I campi numerici hanno lunghezza fissa (2 per INTEGER,
                       4 per REAL
e 8 per LONG REAL). Invece la lunghezza dei
                       campi STRING è in funzione dell'applicazione.


◊ CLOSE(numero_file|ALL)

Chiude il file individuato da "numero_file". Usando l'identificatore "ALL" vengono chiusi tutti i file.

◊ SEEK(numero_file,numero_record)

Posiziona il puntatore al record individuato da "numero_record" nell' ambito del file ad accesso diretto individuato da "numero_file" : deve sempre essere seguito da una lettura o da una scrittura.

"lista_var" e "lista_costanti" indicano rispettivamente una lista di variabili e una lista di costanti separate tra di loro con virgole.

La lettura e la scrittura su file avvengono tramite gli statement INPUT, GET, PRINT seguiti dal carattere "#" e dal "numero_file" con cui si intende interagire. Nel caso di un file ad accesso diretto le variabili devono coincidere in numero con quelle utilizzate dall'istruzione FIELD.

Ad esempio per leggere un file sequenziale e stamparlo sullo schermo:

               PROGRAM LETTURA

               EXCEPTION
                   FERROR%=TRUE    ! si è verificata l'eccezione !
              
    PRINT("Il file richiesto non esiste .....")
               END EXCEPTION

               BEGIN
                   FERROR%=FALSE
                   PRINT("Nome del file";)
                   INPUT(FILE$)          ! chiede il nome del file
              
    OPEN("I",1,FILE$)  ! apre un file sequenziale in lettura
              
    IF NOT FERROR% THEN
                         REPEAT
                            GET(#1,CH$)   ! legge un carattere ....
              
             PRINT(CH$;)   ! ... lo stampa ...
              
          UNTIL EOF(1)    ! ... fine a fine file
                  
END IF
                   PRINT
                   CLOSE(1)                 ! chiude il file
              
END PROGRAM

-- Altri statement di ERRE sono:

◊ EXIT IF espressione
◊ EXIT
◊ EXIT PROCEDURE


Permette l'uscita anticipata da un ciclo FOR, REPEAT, WHILE o LOOP se il valore di "espressione" è TRUE. Nella forma EXIT l'uscita dal ciclo è incondizionata, mentre nell'ultima forma consente l'uscita incondizionata da una procedura.

◊ CONTINUE FOR | WHILE | REPEAT | LOOP

Trasferisce il controllo dall'interno di un ciclo FOR, WHILE, REPEAT o LOOP all'iterazione successiva del ciclo. Nel caso siano presenti più cicli annidati dello stesso tipo, CONTINUE trasferisce il controllo all'iterazione successiva del ciclo più interno.

◊ SHELL(espressione_stringa)

Richiama il Sistema Operativo "target" per eseguire il comando identificato da "espressione_stringa": non viene effettuato nessun controllo sulla sintassi dell'argomento.

◊ CLEAR

Azzera tutte le variabili locali in una procedura. Se usato nel main non ha nessun effetto.

◊ POKE(espressione1,espressione2)

Pone il valore dato da "espressione2" nella cella indicata dal valore di "espressione1": l'insieme di validità di questi valori dipendono in modo esplicito dalla lunghezza di parola e dalla capacità di indirizzamento del microprocessore "target".

◊ CALL(var1,lista_var)

Fa partire un sottoprogramma scritto nel linguaggio macchina del microprocessore "target" a partire dalla locazione indicata dal valore di "var1" passando come parametri le variabili indicate nella "lista_var" (se esistono).


Nota: Esiste un corrispondente funzionale di questo comando che è la funzione USR: è stata mantenuta solo per motivi di compatibilità con la versione per C-64.


◊ CHAIN(nome_file[,ALL])

Permette il caricamento di "nome_file" e la sua esecuzione automatica; con la clausola "ALL" consente di passare tutte le variabili dal modulo chiamante a quello chiamato. Se si invece si vuole passare solo delle variabili selezionate si dovrà utilizzare la clausola COMMON.
CHAIN serve per la gestione a moduli di un programma ERRE e consente un uso ottimizzato delle risorse in quanto un errore in un modulo non costringe a fare ricompilare tutto il programma. Inoltre consente di poter "linkare" programmi scritti in linguaggi diversi, sempreché questi abbiano l'equivalente dello statement CHAIN.

◊ RANDOMIZE(var)

Serve per inizializzare il generatore di numeri casuali. Per avere ogni volta una sequenza diversa utilizzare la variabile di sistema TIMER.

◊ SWAP(var1,var2)

Scambia tra di loro i contenuti delle variabili (di tipo semplice) "var1" e "var2", che devono essere dello stesso tipo.

◊ PAUSE(espressione_numerica)

Consente di bloccare l'esecuzione del programma per un numero di secondi indicato da "espressione_numerica".

ERRE ammette, anche se non è stata specificatamente implementata, la cosiddetta "istruzione nulla" che consente di rendere esplicito il fatto che non viene eseguita nessuna azione: ad esempio

               FOR I%=1 TO 100 DO
               END FOR


è semplicemente un ciclo di attesa.


•  Gli "alias"

ERRE consente anche di utilizzare i cosiddetti "alias" che consistono in abbreviazioni di istruzioni ed operatori. Sono a disposizione i seguenti alias:

 ?
"Sostituisce" PRINT. Ad esempio

               ?(I) è equivalente a PRINT(I)

 @
"Sostituisce" CALL. Ad esempio

               @(X) è equivalente a CALL(X)

 

+= -= *= /= ^=
Consentono di abbreviare delle istruzioni di assegnamento particolari. Ad esempio

               I+=1 è equivalente a I=I+1
               I*=(J-1) è equivalente a I=I*(J-1)


•  Scrittura di un programma ERRE

Un programma ERRE può essere scritto liberamente usando come separatore tra i vari statement uno o più caratteri " " (codice ASCII 32) rispettando queste poche regole:

a) Un'espressione non può avere spazi bianchi al suo interno eccezion fatta per gli operatori AND, OR, NOT, DIV, MOD e IN.

b) Una istruzione di assegnamento o una dichiarazione non può contenere alcun spazio bianco di separazione al suo interno.

c) Tutti le altre istruzioni devono essere separate da uno o più spazi bianchi da ciò che segue o devono essere subito seguite da una parentesi rotonda aperta.

Alcuni esempi chiariranno questi tre punti: scrivere

           A = B+C

è sbagliato per la regola b) in quanto compaiono spazi bianchi prima e dopo il segno di assegnamento, percio si scriverà A=B+C; ugualmente sbagliato è scrivere

           PRINT ("A=";2*Z-3)

per la regola c) in quanto bisogna subito far seguire la parentesi rotonda dopo lo statement PRINT.

Nonostante che tutti gli esempi siano stati scritti in maiuscolo, da questa versione di Erre System è possibile scrivere i sorgenti ERRE anche in minuscolo: la trasformazione in maiuscolo, quando richiesto, sarà totalmente svolta dal sistema.

 

Gestione delle eccezioni
 

Quando si verifica una eccezione questa viene riconosciuta come tale, e provoca la sospensione definitiva dell'esecuzione di un programma: il programmatore può, se lo ritiene opportuno, tramite una sequenza opportuna di istruzioni, modificare questo comportamento e provvedere alla propria gestione delle eccezioni usando un particolare statement che ERRE mette a disposizione:

  EXCEPTION
        ┌───────────────┐
        │  B l o c c o  │
        └───────────────┘
  END EXCEPTION


Questa sequenza di istruzioni può venire dichiarata una sola volta all'interno di un modulo e, quando si verifica l'eccezione, si sostituisce a tutto il resto del modulo stesso: da questo punto di vista la sequenza di istruzioni che gestisce le eccezioni può essere considerata come una procedura che viene attivata soltanto in casi eccezionali.
Ogni tipo di eccezione viene identificato da un numero che viene automaticamente posto nella variabile riservata "ERR" al verificarsi dell'eccezione stessa: perciò ERR può essere consultata per scoprire l'origine dell'errore e per porvi rimedio. Con l'istruzione

      RESUME etichetta

è possibile fare riprendere l'esecuzione dall'etichetta specificata (che deve comunque essere collocata nel main), in caso contrario  al termine del blocco EXCEPTION  avviene il ritorno  all'esecuzione "normale" e precisamente all'istruzione che segue quella che ha provocato l'errore.  Riprendendo l'esempio del file sequenziale al § 2.2.11 vediamo come si articola il flusso dell'esecuzione:


    PROGRAM LETTURA

  --- EXCEPTION
  |     FERROR%=TRUE       ! si è verificata l'eccezione !
  |     PRINT("Il file richiesto non esiste .....")
  |   END EXCEPTION --|    ┌──────────────────────────────────┐
  ▲                   |--►-│ dopo aver eseguito il codice del │---------
  |                     (2)│ blocco EXCEPTION e mancando una  │        |
  |                        │ istruzione RESUME l'esecuzione   │        |
  |                        │ riprende da ..                   │        |
  |                        └──────────────────────────────────┘        |
  |   BEGIN                                                            |
  |     FERROR%=FALSE                                                  |
  |     PRINT("Nome del file";)                                        |
  |     INPUT(FILE$)      ! chiede il nome del file                    ▼ (3)
  |     OPEN("I",1,FILE$) ! apre un file sequenziale in lettura        |
  |                 ┌──────────────────────────────────────────────┐   |
  |                 │ se il file non viene trovato, viene generata │   |
  ----------◄-------│ una eccezione (53 - "File non trovato") e    │   |
           (1)      │ l'esecuzione trasferita al blocco EXCEPTION..│   |
                    │ END EXCEPTION  ......                        │   |
                    └──────────────────────────────────────────────┘   |
        IF NOT FERROR% THEN  -----------◄-------------------------------
          REPEAT
            GET(#1,CH$)   ! legge un carattere ....
            PRINT(CH$;)   ! ... lo stampa ...
          UNTIL EOF(1)    ! ... fine a fine file
        END IF
        PRINT
        CLOSE(1)          ! chiude il file
    END PROGRAM


Nota: Oltre alla variabile riservata ERR esiste anche una "ERL"  (non inclusa nello standard del  linguaggio)  che fornisce  il numero  dell'istruzione del file ERRE *compilato* che ha provocato l'errore e può essere perciò utilmente gestita solo a livello dell'interprete di R-Code.

 Gestione degli eventi

L'esecuzione di un programma ERRE, come vista finora, è di tipo "interno" nel senso  che il suo percorso  dipende da condizioni (test condizionali,  cicli, gestione degli errori ecc..) che si verificano all'interno del programma stesso, una volta forniti i dati di ingresso.  In realtà esistono ambiti applicativi (i cosidetti "real-time")  nei  quali  l'esecuzione viene influenzata da particolari "eventi" provenienti dall'esterno.  Tali eventi possono essere di tipo molto diverso tra loro ed esistono dei linguaggi di programmazione detti per l'appunto "real-time" che ne consentono un trattamento specifico.

ERRE che, ricordiamolo, è un linguaggio di tipo generale consente comunque di trattare tali eventi in maniera uniforme utilizzando l'istruzione EVENT nelle due forme possibili:

   ● EVENT("messaggio")

   ● IF EVENT("messaggio") THEN <azione>

"messaggio"  è  una stringa di comando  che deve essere  risolta a livello di interprete di R-Code: è quindi dipendente dall'implementazione; mentre <azione> è il nome di una procedura che specifica quale deve essere il trattamento dell'evento  una volta che questo sia stato "intercettato" ("trapping" in inglese): l'istruzione EVENT determina lo stato dell'evento (attivato, disattivato o fermo) mentre la IF EVENT determina l'azione da svolgere.

Nel caso del PC IBM tali "messaggi" possono comprendere i seguenti casi:

  •   TIMER(n)     legato all'orologio di sistema (n: in secondi)

  •   KEY(n)       legato alla tastiera (n: tipo tasto)

  •   COM(n)       legato alla porta seriale (n: porta 1 o 2)

  •   PEN          legato alla penna ottica

  •   PLAY(n)      legato all'esecuzione di musica di sottofondo (n: num. note
                   di sottofondo)

  •   STRIG(n)     legato ai joystick (n: tasto premuto)
                   Nota:  può essere usato anche con il mouse se questo lavora
                   in modalità di emulazione joystick.

Un esempio  tratto  dalla distribuzione  chiarirà meglio  il concetto: questo programma aggiorna ogni due  secondi, tramite  la procedura  SHOW_TIME, l'ora visualizzata  in alto  a sinistra,  mentre  l'esecuzione prosegue. Il rientro dalla procedura SHOW_TIME avviene all'istruzione che segue quella che ha "subito" il richiamo dell'evento (che non è determinabile a priori).  

PROGRAM EVENT

!$INCLUDE="PC.LIB"
 
PROCEDURE SHOW_TIME
   OLDROW=CSRLIN         ! salva la posizione del cursore
   OLDCOL=POS(0)         ! (riga e colonna)
   LOCATE(1,1) PRINT(TIME$)
   LOCATE(OLDROW,OLDCOL) ! ripristina cursore all'uscita
END PROCEDURE

BEGIN
   CLS
   IF EVENT("TIMER(2)") THEN SHOW_TIME ◄----- ogni due secondi richiama
                                              la procedura SHOW_TIME
   EVENT("TIMER ON")                   ◄----- abilita l'evento TIMER
                                              con l'apposito "messaggio"uot;
   FOR I=1 TO 10000 DO \
      LOCATE(10,1)      |  ognuna di queste istruzioni può "subire"
      PRINT(I,I*I)      |  l'evento che provoca il salto alla SHOW_TIME
   END FOR             /
 
   EVENT("TIMER OFF")                  ◄----- disabilita l'evento TIMER
END PROGRAM                                   con l'apposito "messaggio"
 


Espressioni e funzioni predefinite
 

Una espressione è formata da variabili, da operatori aritmetico/relazionali, li, da operatori logici e da letterali che in ERRE indicano costanti scritte con il proprio valore invece che con un nome (numerici e stringa) e funzioni sia dichiarate dall'utente che predefinite, poste a disposizione del programmatore dal linguaggio.
Le variabili e i loro tipi sono stati trattati prima, mentre gli operatori logici e aritmetico/relazionali a disposizione sono dettagliati nelle due tabelle più avanti.
Nella tabella 3 l'operatore "^" indica l'elevamento a potenza, l'operatore "DIV" indica la divisione intera e l'operatore "MOD" il resto della divisione intera.
Inoltre viene definito l'operatore logico unario IN nelle forme

               <espressione> IN cost1..cost2

oppure

               <espressione> NOT IN cost1..cost2

che vengono risolte per prime usando gli operatori logici AND e OR.

La gerarchia secondo la quale viene valutata una espressione, quindi, è la seguente:

     1) operatore IN e/o NOT IN
     2) chiamate di funzioni, sia di sistema che definite dall'utente
     3) operatori aritmetici in questo ordine:

          ^ + (unario) - (unario) * / DIV MOD + -

     4) operatori relazionali
     5) operatori logici in questo ordine:

                    NOT AND OR

Questo ordine di valutazione può essere alterato usando opportunamente coppie di parentesi rotonde tenendo conto che le operazioni allo stesso livello vengono eseguite in ordine da sinistra a destra iniziando dalle parentesi più interne come si può vedere dall'esempio seguente limitato agli operatori aritmetici:

     A*B/C-(DELTA^-Z+A)/(C+D)

     1) si valuta -Z ....
     2) ... poi DELTA^-Z
     3) contemporaneamente le due parentesi rotonde
     4) in ordine la moltiplicazione e le due divisioni e...
     5) ... infine la sottrazione
 

┌────────────────────────────────────────────────┐
│                
OPERATORI UNARI               
├────────────────────────────────────────────────┤
│ Operatore         Operando        Risultato    │
├────────────────────────────────────────────────┤
│      +             Numerico        Numerico    │
│      ─             Numerico        Numerico    │
│     NOT            Boolean         Boolean     │
│                    Integer         Integer     │
│  IN (NOT IN)       Boolean         Boolean     │
│                    Integer         Integer     │
└────────────────────────────────────────────────┘

    Tab. 2 : Tipi e operatori unari

┌────────────────────────────────────────────────┐
│                  
OPERATORI BINARI             │
├────────────────────────────────────────────────┤
│ Operatore           Operandi       Risultato  

├────────────────────────────────────────────────┤
│  AND OR XOR          Boolean        Boolean    │
│                      Integer        Integer    │
│    = <> <                                      │
│   <= >= >            Boolean        Boolean    │
│      +               qualsiasi      qualsiasi  │
│   ─ * / ^            Numerico       Numerico   │
│   DIV MOD            Numerico       Numerico   │
└────────────────────────────────────────────────┘

       Tab. 3 : Tipi e operatori binari
 


Gli operatori logici AND, OR e NOT oltre all'utilizzo precedentemente discusso, hanno anche la possibilità di operare sui singoli bit di una variabile o una costante di tipo integer (cioè su 16 bit). Dall'algebra booleana abbiamo le seguenti tabelle (dove V=vero=1, F=falso=0):
 

      Operatore     Valore A Valore B  Risultato    

NOT           V                 F
              F                 V

AND           V        V        V
              V        F        F
              F        V        F
              F        F        F

OR            V        V        V
              V        F        V
              F        V        V
              F        F        F

 

XOR           V        V        F
              V        F        V
              F        V        V
              F        F        F

 

L'operatore XOR, cioè l'OR-ESCLUSIVO, può essere reso usando anche i tre fondamentali:

                                  A XOR B ==> (A AND NOT B) OR (B AND NOT A)

I numeri negativi vengono usati in complemento a due, perciò ad esempio -2 è uguale a 1111111111111110.

Vediamo degli esempi (per semplicità su 8 bit):
 
63 AND 16 = 16 63=%00111111 e 16=%00010000, perciò applicando la tabella precedente dell'AND otteniamo %00010000 e quindi 16 in decimale
4 OR 2 = 6 4=%00000100 e 2=%000000010, perciò applicando la tabella precedente dell'OR otteniamo %00000110 e quindi 6 in decimale.
-1 OR -2 = -1 ricordando l'utilizzo del complemento a due vediamo che -1= 11111111 e
-2=11111110, per cui otteniamo 11111111 che è -1 in decimale.
X% AND 1  vede se X% è pari (=0) o dispari (=1) interrogando il bit di posizione 0 e, più in generale, X% AND (2^I%) trova il bit di posizione I% della variabile X%.


Sussiste anche la relazione NOT X=-(X+1)

L'utilizzo principale di AND è per "mascherare" dei bit e poter interrogare delle strutture dati impaccate (ad esempio lo stato di una porta di I/O): dal punto di vista insiemistico può essere visto come l'equivalente dell'intersezione; mentre OR si usa per "unire" due byte per creare una valore particolare - è l'equivalente dell'unione insiemistica.

I letterali numerici decimali sono rappresentati dai classici numeri, ad esempio

               34 , -17.3 , 1.23E-09, 5.11D-11

E' possibile, in caso di ambiguità, porre un '#' per identificare un letterale in doppia precisione rispetto ad uno in semplice precisione.

Invece i letterali numerici non decimali possono essere solo interi e sono  rappresentabili in base 2, in base 8 e in base 16; per distinguerli si antepone alla costante rispettivamente i simbolo "%", "&" e "$". Ad esempio

               $C000 equivale al decimale 49152
               -$FF equivale invece a -255
               %1000 equivale a 8
               -%111 equivale a -7
               &7777 equivale a 4095

I letterali stringa sono insiemi di caratteri delimitati dal doppio apice ", ad esempio

               "pippo" , "1+2-ABC"

Un letterale speciale è, infine,
п che permette di utilizzare direttamente la nota costante 3.14159....: п è una costante LONG REAL.

Le funzioni predefinite sono le seguenti con il significato elencato di seguito:

ABS(X) : valore assoluto di X
ACS(X) : arcocoseno di X
ASC(X$): codice ASCII di X$
ASN(X) : arcoseno di X
ATN(X) : arcotangente di X
COS(X) : coseno di X
EOF(X) : restituisce TRUE se il file con file_number X è finito.
EXP(X) : esponenziale di X (e^X)
FACT(X): fattoriale di X: se X non è intero viene arrotondato.
FRC(X) : parte frazionaria di X
FRE(X) : memoria disponibile e ottimizzazione dello spazio occupato dalle

         variabili string.
INT(X) : parte intera di X
INSTR(N%,X$,Y$) : ricerca la prima stringa Y$ in X$ a partire dal

         carattere N% e restituisce la posizione in cui la stringa è
         stata individuata.
LBOUND(nome,N) : restituisce gli indici minimi dell'array statico "nome"

         per tutte le N dimensioni possibili dell'array: abitualmente
         questa funzione restituisce sempre 0, salvo quando viene usata
         una opportuna direttiva di compilazione.

         Se N=0 viene restituito il numero delle dimensioni dell'array.

LEN(X$): lunghezza di X$
LOC(X) : restituisce  la posizione corrente nel file con numero_file X:
         se il file è ad accesso diretto viene restituito l'ultimo record
         letto o scritto, se è sequenziale il numero di blocchi da 128
         byte letti o scritti, se è di tipo COM il numero dei caratteri
         che attendono di essere letti.

LOF(X) : restituisce la lunghezza del file con numero_file X.
LOG(X) : logaritmo in base e di X
PEEK(X): contenuto della locazione di memoria X
POS(X) : indica la prossima colonna di stampa per PRINT
RND(X) : generatore di numeri casuali (tra 0 e 1); viene inizializzato
         con un argomento negativo o utilizzando l'istruzione RAMDOMIZE.
SGN(X) : funzione segno di X
SIN(X) : seno di X
SQR(X) : radice quadrata di X
TAN(X) : tangente di X
UBOUND(nome,N): restituisce gli indici più elevati dell'array statico
         "nome" per tutte le N dimensioni possibili dell'array. Se N=0
         viene restituito il numero delle dimensioni dell'array. Per
         gli array dinamici, essendo note le loro dimensioni solo
         a runtime, UBOUND restituisce sempre 0.
USR(X) : esegue una routine scritta per il microprocessore "target",
         passando X come valore: è stata mantenuta solo per motivi di
         compatibilità con la versione per C-64. L'indirizzo di partenza
         della routine deve essere definito tramite una opportuna
         direttiva di compilazione.

VAL(X$): converte X$ in una variabile numerica.

VARPTR(X|X$|#N): restituisce l'indirizzo di memoria ("puntatore") dove è
         memorizzata X (o X$). Se l'argomento è un numero di file, viene
         restituito l'indirizzo del F.C.B. ("File Control Block")
         relativo. Il F.C.B. contiene tutte le informazioni di servizio
         relative al file.


che, essendo X una variabile di tipo numerico e X$, Y$ due variabili di tipo STRING, forniscono un risultato di tipo numerico (REAL o LONG REAL) e

CHR$(X%)       : stringa di un carattere avente X% come codice ASCII
LEFT$(X$,I%)   : primi I% caratteri di X$
MID$(X$,I%,J%) : J% caratteri di X$ a partire da quello di posto I%
RIGHT$(X$,I%)  : ultimi I% caratteri di X$
STR$(X)        : converte X in una variabile stringa.
STRING$(X,Y$)  : restituisce una stringa formata da X elementi Y$
.

                               Ad esempio STRING$(5,"A") restituisce "AAAAA".

che forniscono un risultato di tipo stringa.
Nota: La funzione MID$ può anche essere usata a sinistra del segno di assegnamento per sostituire parti di una stringa:  ad esempio  se A$="1234567890" allora  MID$(A$,4,1)="D" sostituirà in A$ il carattere "4" con  il  carattere "D".


Esistono infine sette variabili riservate e otto costanti riservate:

TIME$,DATE$ : gestiscono ora e data del sistema
TIMER       : orologio interno (aggiornata dal sistema)
ERR         : gestisce il controllo degli errori di esecuzione

              (aggiornata dal sistema)

FREEFILE    : fornisce il numero del primo buffer di I/O libero

              permettendo di aprire un file in sicurezza.
GETKEY$     : equivalente funzionale dello statement GET.
CMDLINE$    : si interfaccia con il Sistema Operativo "target" e
              ritorna la linea comandi usata per l'esecuzione del

              programma.

_________________________________________________________________________


TRUE e FALSE: valori booleani (rispettivamente -1 e 0)

EMPTY       : valore nullo per una variabile di tipo 'variant'

MAXINT, MAXREAL e MAXLONGREAL: valori massimi rappresentabili per i

              rispettivi tipi numerici

MACHINE$    : identifica il tipo di computer che ospita ERRE System.
              Viene inizializzato automaticamente dal compilatore.
              Al momento può assumere due valori:
                  ● PCIBM per computer PC IBM e compatibili.
                  ● CBM64 per computer Commodore 64.

VERSION$    : identifica la versione del linguaggio (30 per il PC e 32
              per il C-64)


Si propongono a conclusione, tre tabelle che riassumono gli statement e gli identificatori (procedure, funzioni e costanti) predichiarati del linguaggio.
 

┌───────────────────────────────────────────────┐

│             PAROLE CHIAVE DI ERRE             │

├───────────────┬───────────────┬───────────────┤

│  AND          │  FOR          │  OTHERWISE    │

│  BEGIN        │  FOREACH      │  PROCEDURE    │

│  CASE         │  FORWARD      │  PROGRAM      │

│  CLASS        │  FUNCTION     │  REDIM        │

│  COMMON       │  GOTO         │  REPEAT       │

│  CONST        │  IF           │  RESUME       │

│  CONTINUE     │  IN           │  STEP         │

│  DIM          │  IS           │  THEN         │

│  DIV          │  LABEL        │  TO           │

│  DO           │  LOCAL        │  TYPE         │

│  ELSE         │  LOOP         │  UNTIL        │

│  ELSIF        │  MOD          │  USES         │

│  END          │  NEW          │  VARIANT      │

│  EVENT        │  NOT          │  WHILE        │

│  EXCEPTION    │  OF           │  WITH         │

│  EXIT         │  OR           │  XOR          │

└───────────────┴───────────────┴───────────────┘
Tab. 4: Elenco delle parole riservate di ERRE
 

┌──────────────────────────────────────────────┐

      PROCEDURE PREDICHIARATE DI ERRE        │

├───────────────┬──────────────┬───────────────┤

│  CALL         │  OPEN        │  SHELL        │

│  CHAIN  (1)   │  PAUSE       │  SWAP         │

│  CLEAR        │  POKE        │  WRITE        │

│  CLOSE  (1)   │  PRINT       │               │

│  DATA         │  RANDOMIZE   │               │

│  FIELD        │  READ        │               │

│  GET          │  RESTORE     │               │

│  INPUT  (2)   │  SEEK        │               │

└───────────────┴──────────────┴───────────────┘

Tab. 5: Elenco delle procedure predichiarate di ERRE
Note: (1) può utilizzare l'identificatore riservato ALL
          (2) può utilizzare l'identificatore riservato LINE
 

┌───────────────────────────────────────────────┐

│   FUNZIONI PREDICHIARATE E SPECIALI DI ERRE   │

├───────────────┬───────────────┬───────────────┤

│  ABS          │  LEFT$        │  UBOUND       │

│  ACS          │  LEN          │  USR          │

│  ASC          │  LOC          │  VAL          │

│  ASN          │  LOF          │  VARPTR       │

│  ATN          │  LOG          │               │

│  CHR$         │  MID$         ├───────────────┤

│  CMDLINE$     │  PEEK         │  EMPTY     

│  COS          │  POS          │  FALSE     

│  DATE$        │  RIGHT$       │  MACHINE$  

│  EOF          │  RND          │  MAXINT    

│  ERR          │  SGN          │  MAXREAL   

│  EXP          │  SIN          │  MAXLONGREAL

│  FACT         │  SPC          │  TRUE      

│  FRC          │  SQR          │  VERSION$  

│  FRE          │  STR$         ├───────────────┤

│  FREEFILE     │  STRING$      │  CHOOSE       │

│  GETKEY$      │  TAB          │  IIF          │

│  INSTR        │  TAN          │  SWITCH       │

│  INT          │  TIME$        │               │

│  LBOUND       │  TIMER        │               │

└───────────────┴───────────────┴───────────────┘

Tab. 6 : Elenco delle funzioni predichiarate di ERRE
Nota: indica le costanti predefinite.
 

Direttive per il compilatore
 

E' possibile inserire ovunque nel testo del programma delle direttive di compilazione sfruttando il carattere di commento:

         
!$direttiva

Per le direttive definite vedere la documentazione relativa al compilatore.
 


Librerie di sistema
 

Le nuove esigenze di programmazione si spingono fino a coprire settori tradizionalmente tenuti in disparte quali grafica in alto risoluzione e suono, che un linguaggio di programmazione deve essere in grado di coprire.
A causa della mancanza di standardizzazione queste scelte non vengono effettuate in modo comune producendo all'interno di ogni linguaggio dei dialetti a volte molto diversi tra loro.
Questo pericolo viene superato dotando il linguaggio della possibilità di inserire delle librerie di sistema che coprono questi interessi di applicazione che saranno diversi da computer a computer: ERRE ammette questa possibilità e per ogni tipo di calcolatore accanto al linguaggio viene rilasciata una libreria di procedure e funzioni che può essere usata dall'utente richiamandola tramite l'apposita direttiva di compilazione.
Questa libreria copre in modo completo gestione dello schermo, grafica e suono.

 

Versione per C-64
 

La versione per C-64 è più ridotta rispetto a quella per PC qui esposta. I manuali del linguaggio e soprattutto il mio libro "PROGRAMMARE IN ERRE" indicano comunque come poter "tradurre" le istruzioni non supportate per garantire la portabilità.

 

Gestione dei Tipi Astratti di Dati

Viene introdotto  il concetto di tipo astratto di dato tramite la definizione  di "classe"  e la dichiarazione di "oggetti" che  si  riferiscono a quella classe: per fare ciò si utilizza lo statement  CLASS...END CLASS in unione allo statement NEW. Una classe è così definita:

   CLASS <nome_classe>
     {dichiarazione di variabili e/o array locali alla classe con LOCAL}
     {definizione e corpo di function e/o procedure per utilizzare la classe}
   END CLASS

Le variabili locali dichiarate tramite "LOCAL" o "LOCAL DIM" possono essere utilizzate solo all'interno delle function e/o procedure  successive (i cosiddetti "metodi") mentre lo scambio di dati con il mondo esterno avviene solo tramite i parametri delle function/procedure stesse.

Una volta definita la classe, si possono dichiararne le variabili relative ("istanze") tramite lo statement

   NEW <var>:<nome_classe>

Con  l'introduzione del concetto di classe ERRE  non diventa ovviamente un linguaggio "object-oriented" ma consente comunque di "utilizzare" oggetti. Vediamo un esempio, sempre tratto dalla distribuzione, dove viene definito una classe (QUEUE) che implementa un oggetto di tipo "stack" con i relativi "metodi" per poterlo utilizzare praticamente. Questo programma stampa in ordine inverso una lista di numeri utilizzando una procedura ricorsiva.

PROGRAM CLASS_DEMO

   CLASS QUEUE               ◄---- definizione di classe
      LOCAL SP               ◄---- con le variabili locali
      LOCAL DIM STACK[100]         utilizzate dai "metodi"

      FUNCTION COUNT()       ◄---- qui iniziano i metodi: il
         COUNT=SP                  programma principale li utilizzerà
      END FUNCTION                 senza vedere la loro struttura interna.

      PROCEDURE INIT         ◄---- inizializzazione della classe:
         SP=0                      gli oggetti dichiarati successivamente
      END PROCEDURE                faranno riferimento a questo metodo per
                                   implementare il proprio "constructor"

      PROCEDURE POP(->XX)    ◄---- il metodo POP restituisce un valore
         XX=STACK[SP]              all'esterno
         SP=SP-1
      END PROCEDURE

      PROCEDURE PUSH(XX)     ◄---- il metodo PUSH riceve un valore
         SP=SP+1                   dall'esterno
         STACK[SP]=XX
      END PROCEDURE
 
   END CLASS
 
   NEW PILA:QUEUE            ◄---- viene dichiarato l'oggetto PILA 
                                   riferito alla classe QUEUE
 
   PROCEDURE STAMPA_INV      ◄---- inizio del programma vero e proprio
     READ(NUM)
     IF NUM<>-1 THEN  ! tappo
        PILA_PUSH(NUM)       ◄---- NUM viene passato per l'elaborazione
        STAMPA_INV                 all'oggetto PILA tramite il metodo
     END IF                        PUSH
     IF PILA_COUNT()<>0 THEN ◄---- idem come sopra solo con il metodo COUNT
        PILA_POP(->NUM)      ◄---- idem come sopra solo con il metodo POP
        PRINT(NUM)                 che restituisce NUM.
     END IF
   END PROCEDURE

BEGIN
      DATA(1,3,5,7,9,11,-1)
      PILA_INIT              ◄---- "constructor" dell'oggetto PILA
      STAMPA_INV
      PRINT("Fine")
END PROGRAM

Contrariamente ai linguaggi "object-oriented"  veri e propri non esiste un "destructor"  di oggetti cosicché questi restano, per così dire, "in vita" fino al termine del programma.