elaborazioni simboliche
SNOBOL (StriNg Oriented symBOlic Language) è un linguaggio di programmazione creato nei laboratori Bell, per la manipolazione delle stringhe di simboli, utile in aree come la linguistica, la compilazione di indici e bibliografie, la manipolazione di testi e la trattazione simbolica di espressioni algebriche. Lo sviluppo di SNOBOL, culminato in SNOBOL4 avvenne fra 1l 1962 e il 1967, al progetto lavorarono D. J. Farber, R. E. Griswold, e F. P. Polensky.
Inizialmente il progetto ebbe un nome più goliardico: SEXI (String EXpression Interpreter), poi divenne SNOBOL in assonanza con un’affermazione di uno degli sviluppatori: "This program doesn't have a snowball's chance in hell of ...".
SNOBOL3 è del 1964, SNOBOL3 ha solo il tipo di dato stringa, nel 1966 SNOBOL4 aggiunge altri tipi di dato ed è completato nel 1967, sebbene qualche funzionalità, come la ridefinizione degli operatori, è del 1969. In questo periodo, riscritto in macro assembler per una macchina astratta, diventa facilmente portabile e quindi disponibile su differenti calcolatori.
I tipi di dato di SNOBOL sono stringhe, numeri interi e reali, tabelle associative, matrici di qualsiasi dimensione e tipi definibili dall’utilizzatore. Inoltre il tipo di dati pattern, introdotto in SNOBOL4, permette di memorizzare un modello dei dati, ad esempio COLORE = 'BLU' | 'ROSSO' può essere usato per controllare se una stringa contiene uno dei due colori.
I programmi sono compatti, grazie alla struttura delle istruzioni, alle variabili oggetto INPUT ed OUTPUT, ed alla possibilità di indicare quale istruzione eseguire nel caso di successo o fallimento di un test o di un’istruzione:
LEGGI OUTPUT = INPUT :S(LEGGI)
L’istruzione precedente, che legge e scrive un file, contiene tutti gli elementi sintattici delle istruzioni SNOBOL, vale a dire un’etichetta, un comando e l’indirizzo della successiva istruzione da eseguire. Le variabili INPUT ed OUTPUT sono collegate agli standard input ed output ed il loro utilizzo corrisponde rispettivamente ad una lettura ed a una scrittura. In quanto all’eventuale indirizzo dell’istruzione successiva, esso può essere incondizionato o condizionato dal successo o fallimento del comando; nel caso di lettura di file :F(end_of_file) indica che al fallire della lettura, il controllo passa all’istruzione con label end_of_file. In caso di successo, di un test o di un match, :S(label) indica di passare il controllo alla label label.
SNOBOL è un linguaggio interpretato, ed utilizza questa caratteristica tramite le istruzioni EVAL(istruzione) e CODE(istruzioni) per eseguire frammenti di programma generati dinamicamente, per trattare come variabili i nomi di dati, mediante il prefisso $:
? PIEMONTE = 'TORINO'
? TORINO = 'PIEMONTE'
? OUTPUT = 'La capitale del ' $PIEMONTE
" è " PIEMONTE
La capitale del PIEMONTE è
TORINO
Il prefisso $ permette anche di trasferire il controllo ad un’etichetta specificata da una variabile.
I comandi condizionali hanno la forma di funzioni, quali IDENT(a,b), GT(op1,op2); il cui risultato è la stringa nulla o il fallimento dell’istruzione. Questo semplice meccanismo non prevede test complessi, tuttavia la generazione della stringa nulla in caso di successo, permette di scrivere sia un and di condizioni sia un’assegnazione condizionata:
DEFINE('FATT(N)LCLVAR')
OUTPUT
= 'FATT 7 = ' FATT(7)
...
FATT LCLVAR =
1
FATT1 LCLVAR =
LCLVAR * N
N =
GT(N,1) N - 1 :S(FATT1)
FATT = LCLVAR :(RETURN)
FATT 7 = 5040
L’esempio evidenzia anche la possibilità di definire funzioni, anzi il prototipo di una funzione non è una semplice dichiarazione, ma deve essere eseguito; in esso si possono indicare le variabili locali. Il ritorno da una funzione avviene tramite il goto :(RETURN) o :(FRETURN) per indicare un ritorno con o senza fallimento.
L’analisi o match di una stringa è semplicemente la giustapposizione di essa con il modello; durante l’analisi parti della stringa possono essere assegnate a delle variabili o essere sostituite:
? B = '4
+ (5 - a * (7 + c)) * 12'
? B
BREAK("(") BAL . OUTPUT . OUT
(5 - a * (7 + c))
? OUT
POS(1) BREAK("(") BAL . OUTPUT
(7 + c)
BREAK() esegue il
match fino al o ai caratteri indicati, nell’esempio "(",
successivamente BAL esegue il match della stringa bilanciata
rispetto alle parentesi tonde. Il . (punto) è un operatore per assegnare
la stringa che soddisfa il match ad una variabile, nel nostro caso OUTPUT
e OUT
conterranno (5 - a * (7 + c)). L’istruzione successiva, per estrarre
il valore fra parentesi più interno, è analoga al match precedente, tranne per
il fatto che il match inizia a POS(1), dopo il carattere (.
Oltre alle funzioni BREAK(), POS() e al modello predefinito BAL visti nell’esempio precedente, ci sono:
· LEN(intero), è la stringa di intero caratteri.
Nel frammento che segue, il modello PT cerca due caratteri uguali contigui: LEN(1) è il primo che sarà memorizzato tramite $ X nella variabile X, e poi sarà confrontato con il carattere successivo *(X). L’asterisco e le parentesi hanno la funzione di inibire la valutazione di X al momento della creazione del modello, valutazione che viceversa sarà effettuata al momento dell’utilizzo del modello. L’applicazione del modello alla variabile Z provocherà la sostituzione delle coppie di caratteri uguali con il singolo carattere contenuto nella variabile X.
Z = 'ECCEZZIONALLMENNTE'
PT = LEN(1) $ X
*(X)
OUTPUT
= Z
LOOP Z PT = X :S(LOOP)
OUTPUT = Z
ECCEZZIONALLMENNTE
ECEZIONALMENTE
Una serie di parole chiave permettono di modificare alcuni aspetti del linguaggio (&TRACE, &TRIM, …) o offrono sottoinsiemi predefiniti di caratteri come &ALPHABET, &LCASE o permettono di accedere alla riga di comandi &PARM.
SNOBOL è stato portato su diversi calcolatori: IBM 360, CDC 6600, GE 635, UNIVAC 1108, RCA Spectra 70, Ferranti Atlas 2, SDS Sigma 7, DEC PDP-10, Burroughs 6700 e Personal Computer.
REBUS (1985) è SNOBOL4 con sintassi derivata da Icon, REBUS genera un sorgente SNOBOL4.
SPITBOL (SPeedy ImplemenTation of snoBOL) è una versione ampliata e
commerciale di SNOBOL4.
Altre realizzazioni: SNOBOL2, SNOBOL3, SNOBOL4, FASBOL, MAXSPITBOL, ELFBOL, SITBOL, MAINBOL e Vanilla.
Il listato che segue è un preprocessore
che fornisce a SNOBOL le strutture:
REPEAT TIMES n
REPEAT WHILE condizione
REPEAT
FOR vrb = inizio TO fine BY incremento
* Vanilla SNOBOL4 Version 2.22
* REPEAT.SNO * inserisce struttura FOR, WHILE in SNOBOL DEFINE('SCRIVI(LB,ISTR,GO)') &TRIM = 1 * preparo pattern e
variabili TABUL = CHAR(9) ;* tabulazione SPAZI = SPAN(' ' TABUL) ;* accetta uno o più spazi o tabulazioni CKETC = 0 ;* conta repeat STK = '' ;* Stack repeat PAT.PERF = 'REPEAT' SPAZI
BREAK(' ') . TIPO
REM . RESTO PAT.FOR = POS(0) ARB . VRB
'=' ARB . FOR 'TO' ARB . AT 'BY' ARB . BY 'BY' INPUT('INFILE', 1, , 'REPEATPR.SRC') :F(ERR) OUTPUT('OUTFILE', 2, , 'REPEATPR.SNO') :F(ERR) READF LINE = INFILE :F(END) LINE PAT.PERF :F(NO_REPEAT) CKETC
= CKETC + 1 STK = 'ETC' CKETC '_' '.' STK INDICE = 'INDX' CKETC ;* genero variabile di ciclo SCRIVI('*',LINE) IDENT(TIPO,'TIMES') :F(NO_TIMES) SCRIVI(,INDICE ' = ' RESTO) SCRIVI('ETC' CKETC '_S',INDICE ' = ' INDICE ' - 1') LINE = TABUL 'LT(' INDICE ',0) ' TABUL ':S(ETC' CKETC
'_F)' :(OUT) NO_TIMES IDENT(TIPO,'WHILE') :F(NO_WHILE) LINE = 'ETC' CKETC '_S ' RESTO ' :F(ETC' CKETC '_F)'
:(OUT) NO_WHILE IDENT(TIPO,'FOR') :F(NO_REPEAT) (RESTO ' BY 1 BY') PAT.FOR ;* eventuale default SCRIVI(,VRB ' = ' FOR ' - '
BY) SCRIVI('ETC'
CKETC '_S',VRB ' = ' VRB ' + ' BY) LINE
= TABUL 'GT(' VRB ',' AT ')' '
:S(ETC' CKETC '_F)' :(OUT) NO_REPEAT ;* controllo blocco END REPEAT LINE 'END' SPAZI
'REPEAT' :F(OUT) STK BREAK('.') . LB '.' = SCRIVI(,,':('
LB 'S)') LINE = LB 'F' OUT OUTFILE = LINE :(READF) ERR OUTPUT = 'ERRORE DI OPEN' :(END) SCRIVI OUTFILE = LB TABUL ISTR TABUL TABUL GO :(RETURN) END |
Qui di seguito il programma che calcola alcuni numeri
primi, con le strutture introdotte.
* Vanilla SNOBOL4 Version 2.22
* calcolo numeri primi fra
11 e 100 REPEAT
FOR I = 11 TO 100 BY 2 J = 1 REPEAT WHILE GT(I,J * J) J
= J + 2 RESTO = REMDR(I,J) J = EQ(RESTO,0) I
;* stampa se primo END REPEAT OUTPUT = NE(I,J) I END REPEAT END |
Infine il sorgente generato ed il risultato
dell’esecuzione del programma.
* Vanilla SNOBOL4 Version 2.22
* calcolo numeri primi fra
11 e 100 * REPEAT FOR I = 11 TO
100 BY 2 I = 11
- 2 ETC1_S I =
I + 2 GT( I , 100 ) :S(ETC1_F) J = 1 * REPEAT WHILE GT(I,J * J) ETC2_S GT(I,J * J) :F(ETC2_F) J = J + 2 RESTO = REMDR(I,J) J = EQ(RESTO,0) I
;* stampa se primo :(ETC2_S) ETC2_F OUTPUT
= NE(I,J) I :(ETC1_S) ETC1_F END |
11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 |