GUIDA AL LINGUAGGIO ERRE (v 2.6)

ERRE LANGUAGE TUTORIAL (v 2.6)

 

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

 

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.

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.

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 '%'.  Ad 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. Ad 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 '#'. Ad 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. Ad 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. Ad 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, array, record

        e funzioni }

            { [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=(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=(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.

                PROCEDURE nome_proc[(param.ingresso->param.uscita)]

Segue poi la parte esecutiva vera e propria ("corpo")  ed al termine END PROCEDURE.

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) o 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 funzione 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


-- FOR var=(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

            FOR I%=(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

            FOR I$=("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


-- CASE espressione OF
   { 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


E' possibile inoltre specificare, come label, anche degli insiemi numerici usando la notazione "..": per esempio, anziché scrivere come label:

                1,2,3,4,5->

è possibile scrivere

                1..5->

Riferendosi invece 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


--
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 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 (array monodimensionale)  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

 

● 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(["prompt",]lista_var)

Acquisisce valori da tastiera separandoli, eventualmente, da ",", e li attribuisce a "lista_var". La presenza di "prompt" è equivalente a scrivere

 

                 PRINT("prompt";) INPUT(lista_var)

 

◊  LINPUT(["prompt",]lista_var)
 

Legge caratteri da tastiera (incluso il carattere ",") fino al tasto <Invio> e li attribuisce alla variabile stringa "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,lung_record)

Apre un file individuato dalle caratteristiche indicate in "tipo_file": il file può essere sequenziale ("I" per ingresso, "O" per uscita, "A" per accodare in scrittura) o ad accesso diretto ("R"). Solo in questo caso, nella dichiarazione necessita la specifica della lunghezza del record. 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",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)
    CLOSE(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.

◊ Lettura e scrittura di file

 

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).

◊ 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".

◊ Istruzione nulla
 

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)

 +=  -=  *=  /=  ^=
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 (cfr. esempio di lettura di un file sequenziale); al termine della EXCEPTION avviene il ritorno all'esecuzione "normale" e precisamente all'istruzione che segue quella che ha provocato l'errore.
 


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             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

 

Un altro operatore, XOR cioè l'OR-ESCLUSIVO, non è previsto dal linguaggio, ma può essere reso usando i tre precedenti:

                                  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:

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= 1111111111111111 e -2=1111111111111110, per cui otteniamo 1111111111111111 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 (DOUBLE).

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)
FRE(X) : memoria disponibile e ottimizzazione dello spazio occupato dalle

         variabili string.
FRC(X) : parte frazionaria di X
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.
LEN(X$): lunghezza di X$
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 run-time,

         UBOUND restituisce sempre 0. Non esiste una funzione LBOUND  in

         quanto il minimo indice possibile è zero.

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.

         L' 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.

Esistono infine quattro variabili ("di sistema") e sette 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)
----------------------------------------------------------------------------

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

MAXINT, MAXREAL, MAXLONGREAL: massimi valori rappresentabili numerici per i

              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 (26)

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

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

│       ELENCO DELLE PAROLE CHIAVE DI ERRE      │

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

│  AND          │  FOR          │  PROCEDURE    │

│  BEGIN        │  FORWARD      │  PROGRAM      │

│  CASE         │  FUNCTION     │  REPEAT       │

│  CHOOSE       │  GOTO         │  STEP         │

│  COMMON       │  IIF          │  SWITCH       │

│  CONST        │  IF           │  THEN         │

│  CONTINUE     │  IN           │  TO           │

│  DIM          │  LABEL        │  TYPE         │

│  DIV          │  LOCAL        │  UNTIL        │

│  DO           │  LOOP         │  VARIANT      │

│  ELSE         │  MOD          │  WHILE        │

│  ELSIF        │  NOT          │  WITH         │

│  END          │  OF           │               │

│  EXCEPTION    │  OR           │               │

│  EXIT         │  OTHERWISE    │               │

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

Tab. 4: Elenco delle parole riservate di ERRE
 

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

ELENCO DELLE PROCEDURE PREDICHIARATE DI ERRE

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

│  CALL         │  LINPUT      │  SEEK         │

│  CHAIN  (1)   │  OPEN        │  SHELL        │

│  CLEAR        │  PAUSE       │  SWAP         │

│  CLOSE  (1)   │  POKE        │  WRITE        │

│  DATA         │  PRINT       │               │

│  FIELD        │  RANDOMIZE   │               │

│  GET          │  READ        │               │

│  INPUT        │  RESTORE     │               │

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

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

ELENCO DELLE FUNZIONI PREDICHIARATE DI ERRE  │

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

│  ABS          │  INSTR        │  SGN         │

│  ACS          │  INT          │  SIN         │

│  ASC          │  LEFT$        │  SPC         │

│  ASN          │  LEN          │  SQR         │

│  ATN          │  LOF          │  STR$        │

│  CHR$         │  LOG          │  STRING$     │

│  COS          │  MACHINE$   ● │  TAB         │

│  DATE$        │  MAXINT     ● │  TAN         │

│  EMPTY        │  MAXLONGREAL● │  TIME$       │

│  EOF          │  MAXREAL    ● │  TIMER       │

│  ERR          │  MID$         │  TRUE      ● │

│  EXP          │  PEEK         │  UBOUND      │

│  FALSE      ● │  POS          │  VAL         │

│  FRC          │  RIGHT$       │  VARPTR      │

│  FRE          │  RND          │  VERSION$  ● │

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

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à.