SNOBOL

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