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.
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)
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 FOREACH
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 FOREACH
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 FOREACH
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..END IF
--
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$)
-- istruzione di
assegnamento
L'istruzione di assegnamento consente di
attribuire un valore ad una variabile,
semplice o aggregata. ERRE ha disponibili varie forme di assegnamento:
var=espressione
è lo statement generico di assegnamento: "var" è
una variabile semplice o un componente di variabile aggregata e deve essere di tipo compatibile con "expression"
(cfr. § 2.1). Un esempio è
VALUE=X+Y%-2^(1/MATRIX[I%,J%])
E' possibile "abbreviare" questa istruzione usando
gli alias che sono += -= *= /= ^=
Ad esempio
I+=1 è equivalente a I=I+1
I*=(J-1) è equivalente a I=I*(J-1)
I^=2
è equivalente a I=I^2
Altri due casi speciali riguardano le variabili
aggregate: è possibile scrivere
Sono utilizzabili anche le cosiddette "funzioni
speciali" 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
var1[,var2[,...,[.varn]...]]:=espressione
L'operatore ':=' consente di effettuare assegnamenti multipli su
di una stessa linea. Le varie varj sono semplici o componenti
di variabili aggregate. Ad esempio
I=J=K=T:=12
assegna il valore 12 alle variabili in lista (e cioè I,J,K e T) ed è
equivalente a scrivere:
I=12
J=12
K=12
T=12
var_array=var_array1
var_array=(lista)
si usano con le variabili aggregate: "var_array", "var_array1" e "lista"
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. Nel caso di una matrice la
memorizzazione dei dati si intende per "righe" con le singole righe
separate da un ";".
-- 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.
Nota:
I caratteri della tabella dei codici ASCII possono essere idealmente
suddivisi in "caratteri stampabili" e "caratteri non
stampabili": questi ultimi servono per eseguire delle funzioni speciali
che dipendono strettamente dalla
piattaforma. A questo scopo si usa la sequenza
PRINT CHR$(n), dove n è inferiore a 32, così
nel caso del PC è possibile avere:
_______________________________________________________________________
n risultato
_____________________________________________________________________________
7 beep
9 tabulazione
10 riga a capo (line
feed - come 13)
11 cursore in alto a
sinistra
12 cancella lo
schermo (o salto pagina per le stampanti)
13 riga a capo (carriage
return - come 10)
28 sposta cursore a
destra
29 sposta cursore a
sinistra
30 sposta cursore su
31 sposta cursore giù
altri stampano il
carattere corrispondente
_________________________________________________________________________
Gli stessi risultati si possono
ottenere usando la unit "CRT.LIB" richiamando
l'opportuna procedura.
◊ FPRINT(lista_espressioni)
Stampa sullo standard output "lista_espressioni"
secondo la seguente formattazione: le espressioni numeriche vengono
stampate senza l'eventuale spazio bianco
iniziale e le espressioni di tipo stringa vengono stampate racchiuse
tra doppi apici usando "," come separatore. Ad
esempio se
A%=80, B=-90.5, C$="That's all."
e D=-1E-13 allora le istruzioni
FPRINT(A%,B,C$,D)
PRINT(A%,B,C$,D)
stamperanno rispettivamente
80,90.5,"That's all.",-1E-13
80 -90.5
That's all. -1E-13
◊ 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". Facoltativamente può essere
stampato anche un prompt di richiesta
delle
variabili.
Nota: E'
disponibile anche la funzione INPUT$(n) che consente di
acquisire n caratteri dallo standard input.
◊ LINPUT(["prompt",] 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".
Nota: E' disponibile anche la funzione GETKEY$
(senza argomenti), equivalente alla GET, che acquisisce un
carattere dallo standard input ed è utilizzabile, per esempio,
all'interno di un test condizionale.
◊ 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(#numero_file,tipo_file,tipo_accesso,[LEN=lung_record])
Apre un file
utilizzando i
parametri indicati tra parentesi.
"numero_file": è il numero del buffer utilizzato dal file e
dipende strettamente dalla piattaforma (nel caso del PC varia tra 1 e
15). Un #numero_file può essere associato
solo ad un file alla volta: la funzione FREEFILE permette di
conoscere il primo numero libero. La
variabile riservata MAXFILES permette invece di conoscere il
numero massimo di files che possono essere
aperti contemporaneamente.
"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:".
"tipo_file":
il
file può essere sequenziale ("S"), ad accesso diretto ("R"-random)
o di comunicazione ("C"). In questi due ultimi casi, nella dichiarazione necessita la specifica della lunghezza del record.
"tipo_accesso":
indica la modalità di accesso al file, che può essere di tipo
READ, WRITE e READ WRITE, ed il tipo di gestione del file
da parte di altri processi in ambiente distribuito
(SHARED, LOCK READ, LOCK WRITE e LOCK READ WRITE).
Questa seconda caratteristica è facoltativa: se manca, si assume la
proprietà del file da parte del processo che lo utilizza.
"lung_record": l'identificatore LEN= è facoltativo e serve
per migliorare la leggibilità.
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.
◊ Lettura e scrittura su file
La lettura e la scrittura su file avvengono tramite gli statement INPUT,
LINPUT, GET,
PRINT, WRITE 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.
◊ EXEC(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(["messaggio",]espressione_numerica)
Consente di bloccare l'esecuzione del programma per un numero di
secondi indicato da "espressione_numerica". Se "espressione_numerica" è
esplicitamente uguale a 0 il programma resta in attesa di un tasto:
quindi PAUSE(0) equivale ad un ciclo di attesa, ad esempio
REPEAT UNTIL LEN(GETKEY$)<>0. Viene inoltre inviato allo "standard
output" il messaggio indicato, se presente. Ad esempio
PAUSE("Premi un tasto per
continuare....",0)
stampa il messaggio
indicato ed attende la pressione di un tasto per continuare (argomento
numerico esplicitamente uguale a zero).
◊ ACCEPT(espressione_numerica,var1,var2)
Acquisisce caratteri dallo
standard input in modo temporizzato e restituisce nel contempo un
codice di uscita. "espressione_numerica" è il tempo di attesa (in
secondi), "var1" contiene il codice di uscita (0=nessun tasto premuto)
e "var2" il tasto premuto.
PROGRAM ACCEPT_TEST
BEGIN
ACCEPT(3,X%,A$)
IF NOT X% THEN
PRINT("Nessun tasto premuto")
ELSE
PRINT("Codice ASCII:";ASC(A$))
END IF
END PROGRAM
Se entro 3 secondi non si
preme nessun tasto X%=0 ed A$="", altrimenti X%=-1 e A$ il carattere
premuto (di cui il programma stampa il codice ASCII)
◊ @(riga,colonna)
Consente di
indirizzare direttamente il cursore sullo schermo senza caricare la unit
CRT: riga e colonna sono le coordinate-schermo. L'origine delle
coordinate-schermo è in alto a sinistra, partendo dallo zero.
Questa istruzione è stata mantenuta per compatibilità con la versione
per C-64.
◊ CHANGE var1
TO var2
Consente di 'scomporre' una
variabile stringa "var1" nei caratteri che la costituiscono mettendo i
rispettivi codici ASCII nell'array "var2". L'elemento di indice 0
dell'array contiene la lunghezza della stringa. E' possibile anche
effettuare l'operazione inversa (da array a stringa).
PROGRAM
CHANGE_TEST
DIM A$,C$
DIM L%[255]
BEGIN
A$="ABCDEFGHIJKL"
CHANGE A$ TO L%[]
◄---
'scompone' A$
SWAP(L%[1],L%[3])
◄---
scambia il I° ed il III° carattere della stringa
CHANGE L%[] TO C$ ◄---
'ricompone' C$
PRINT(A$,C$)
END PROGRAM
stamperà
ABCDEFGHIJKL CBADEFGHIJKL
Utilizzando solo le funzioni di stringa, e quindi senza CHANGE, il
programma sarebbe stato meno lineare.
Nota: Per motivi di
efficienza l'array "var2" andrà scelto di tipo INTEGER.
◊ 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.
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";
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
┌────────────────────────────────────────────────┐
│
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
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
Nota: Gli operandi di
tipo "Integer" si riferiscono a numeri compresi tra
-MAXINT e +MAXINT, eventualmente arrotondati
(tramite la "conversione automatica di tipo" - come negli esempi
seguenti), altrimenti viene segnalato errore.
25.68 DIV 6.99 ==> 26
DIV 7 = 3
25.68 MOD 6.99 ==> 26
MOD 7 = 5
15.78 OR 3.97 ==> 16
OR 4 = 20
Nota: E’ possibile
utilizzare, per semplificare la scrittura di espressioni di tipo
polinomiale, la funzione POLY nella forma:
POLY(expr,valuen,valuen-1,...,value1,value0)
Ad esempio, Y=X^3-7*X^2+11*X-4
si può scrivere come
Y=POLY(X,1,-7,11,-4)
Se al posto di X è posta una
costante numerica (ad es. 3), allora Y avrà il valore Y=3^3-7*3^2+
11*3-4=-7
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
TRUNC$(X$,N%): toglie gli ultimi N% caratteri da X$.
E' equivalente a
LEFT$(X$,LEN(X$)-N%)
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 nove variabili ("di sistema") 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.
FREEMEM : fornisce la memoria disponibile
per il modulo attualmente
in esecuzione, ottimizzando nel contempo lo spazio occupa-
to dalle variabili STRING.
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.
MAXFILES
: fornisce il numero di file apribili contemporaneamente
durante l'esecuzione di un modulo.
-------------------------------------------------------------------------
TRUE e FALSE: valori booleani (rispettivamente -1 e 0)
EMPTY
: valore nullo per una variabile di tipo 'variant'
MAXINT, MAXREAL e
MAXREAL#: 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
o 26 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 │ SWAP │
│
CHAIN (1) │ PAUSE │
WRITE │
│ CLEAR │ POKE │
@ │
│ CLOSE (1) │ PRINT │
│
│ DATA │ RANDOMIZE
│ │
│ FIELD │ READ
│ │
│ GET │ RESTORE
│ │
│ INPUT │ SEEK
│ │
│ LINPUT │ EXEC
│ │
└───────────────┴──────────────┴───────────────┘
T ab. 5: Elenco
delle procedure predichiarate di ERRE
Note: (1) può utilizzare l'identificatore riservato ALL
┌───────────────────────────────────────────────┐
│
FUNZIONI PREDICHIARATE E SPECIALI DI ERRE
│
├───────────────┬───────────────┬───────────────┤
│ ABS │
GETKEY$ │ RND │
│ ACS │ =IIF
│ SGN │
│ ASC │ INPUT$
│ SIN │
│ ASN │ INSTR
│ SPC │
│ ATN │
INT │ SQR │
│ =CHOOSE │ LBOUND
│ STR$ │
│ CHR$ │
LEFT$ │ STRING$ │
│ CMDLINE$ │ LEN
│ =SWITCH │
│ COS │
LOC │ TAB │
│
DATE$
♦
│ LOF │ TAN │
│ EMPTY ● │
LOG │ TIME$ │
│ EOF │
MACHINE$ ● │ TIMER │
│ ERR │
MAXFILES │ TRUE ● │
│ EXP │ MAXINT
● │ TRUNC$ │
│ FACT │ MAXREAL
● │ UBOUND │
│ FALSE ● │ MAXREAL#
● │ USR │
│ FILEATTR$
│ MID$
♦
│ VAL │
│ =FORMAT$ │
PEEK │ VARPTR │
│ FRAC │
POLY │ VARPTR$ │
│ FREEFILE │
POS │ VERSION$ ● │
│ FREEMEM │
RIGHT$ │ │
└───────────────┴───────────────┴───────────────┘
Tab. 6 : Elenco delle funzioni predichiarate di ERRE
Nota:
● indica le costanti predefinite.
♦
utilizzabile anche
a sinistra del segno di assegnazione
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.
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.
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à.
|