2.28        FORTH

FORTH ha origine da un insieme di programmi di utilità che Charles H. Moore , come una cassetta di attrezzi, si era costruito durante le sue variegate esperienze di programmazione. Il momento in cui tali strumenti cominciano ad assumere la fisionomia di Forth è nel 1968, con una versione scritta in FORTRAN per IBM 1130. Il nome intendeva suggerire un linguaggio di IV generazione (FOURTH in inglese), ma il sistema operativo ospite, di terza generazione, aveva la limitazione dei nomi a cinque caratteri.

In seguito Moore portò Forth su Borroughs 5500, quindi su Univac 1108 e poi, lavorando al National Radio Astronomy Observatory (NRAO), su Honeywell 316, ove fu prodotto anche un compilatore. Nel 1972 Elizabeth Rather , che aveva affiancato Moore al NRAO, scrisse il primo manuale di Forth. L’applicazione sviluppata al NRAO, l’acquisizione dei dati dai telescopi e di fatto il Forth con la quale era scritta, si diffuse presso vari Osservatori, tant’è che nel 1976 Forth fu adottato come linguaggio standard dalla International Astronomical Union.

Nell’arco degli anni 70 Moore, che nel 1973 aveva creato la Forth, Inc, implementò il linguaggio su 18 CPU diverse, con un corredo di assembler, driver per dischi e terminali e subroutines aritmetiche, in particolare per numeri floating point. Altre implementazioni o reimplementazioni furono fatte dal Forth Interest Group (FIG 1978) con l’utilizzo di FIG-Forth, un Forth scritto ex-novo dal gruppo, e posto in public domain. Questo fatto diffuse ancor più Forth e la disponibilità del sorgente facilitò la diffusione su ulteriori computer[1].

Nel 1983 Henry Laxen e Michael Perry creano Forth–83 o F83, una versione pubblico dominio con supporto del multitasking, inserita in un sistema operativo. Nel 1987 i principali produttori di Forth iniziarono la standardizzazione ANSI del linguaggio.

La caratteristica di Forth di non aver necessità del Sistema Operativo per funzionare, lo rese ideale per la programmazione dei microprocessori usati come sistemi di controllo ed inoltre Moore ed altri costruirono microprocessori basati sul Forth, quali Novix NC 4016 (Charles Moore), MF1600 (Alan Winfield), e WISC (Writable Instruction Set Computer) CPU/32 (Phil Koopman). La caratteristica di queste CPU era nelle loro prestazioni eccezionali.

Forth ha avuto un grande successo ed è ancora utilizzato, già le prime versioni di Forth erano multitasking, di tipo cooperativo, il cui vantaggio è la velocità di commutazione dei task ma l’efficienza totale può essere influenzata da task che usano la cpu intensivamente e la monopolizzano.

La semplicità della sintassi di Forth ha favorito il proliferare di interpreti che vanno dagli esercizi amatoriali agli strumenti professionali, ciò ha generato implementazioni che differiscono ampiamente fra di loro, qualitativamente e per le funzionalità disponibili.

Forth è un linguaggio molto vicino alla macchina e si basa su un dizionario che contiene il nome delle funzioni e delle variabili, con relativi indirizzo del codice eseguibile corrispondente o locazione in memoria. L’altra componente fondamentale è una o più memorie stack: sicuramento lo stack dei numeri ed eventualmente lo stack per i numeri floating e per i ritorni da funzione. Il programma è un testo formato da stringhe di caratteri, dette parole (word), separate da spazi. L’interprete cerca la parola nel dizionario in quanto potenziale routine o funzione eseguibile. Se la parola non è nel dizionario ed è riconosciuta come un numero, questo è messo sullo stack. Non c’è nessuna limitazione nella formazione dei nomi delle parole, tant’è che è possibile ridefinire anche le stesse parole primitive. Le parole : (due punti) e ; (punto e virgola) sono le parola per la definizione o ridefinizione di parole; la definizione corrisponde ad una compilazione ed un’inserzione nel dizionario della parola definita:

: ^  ( num -- num) dup * ;   ok[2]

5 ^ . 25  ok

L’esempio (prima riga) definisce la parola ^ come quadrato di un numero. La parola ( è l’inizio di un commento, e in quanto parola deve essere seguita da uno spazio, in questo caso il commento indica il contenuto dello stack prima e dopo l’esecuzione della parola. Tutte le operazioni aritmetiche e di confronto coinvolgono i numeri sullo stack e sostituiscono questi con il risultato, quindi qualsiasi espressione è scritta in notazione postfissa. Essendo il linguaggio basato sullo stack, sono presenti un’insieme di funzioni per operare su di esso:

Il 2 prefissato alle istruzioni indica che esse sono effettuate su coppie di numeri.

In Forth i tipi di dato “primitivi” sono numeri e stringhe; i numeri sono interi, con o senza segno o floating, ed essendo il linguaggio senza il sovraccarico degli operatori, esso è afflitto da una pletora di parole per tutti i diversi operatori aritmetici e relazionali e per tutti i tipi di numero previsti, ad esempio in GFORTH ci sono cinque operatori relazionali minore: <, u<, d<, du< e f<, rispettivamente per numeri interi, interi non segnati, doppi e floating. 

Le dichiarazioni di variabili e costanti sono esemplificate dalla figura Figura 2‑5 :

3.141592E0 fconstant pigreco

definizione costante floating point

: nomestr s" costante stringa" ;

definizione di costante stringa con nome

s" costante stringa"

costante stringa anonima

365 constant giorni

definizione di una costante

variable joe

variabile numerica

create slim 7 ,

variabile numerica con valore assegnato

fvariable raggio

variabile numerica floating

create slim 80 chars allot

riserva spazio per variabile di 80 caratteri

Figura 2-5

La parola constant è fuorviante in quanto nulla vieta che la costante possa essere ridefinita con un altro valore.

L’effetto dell’esecuzione della parola s" è di porre sullo stack la lunghezza e l’indirizzo della stringa che segue, viceversa l’interpretazione degli altri tipi di variabili ne mette a disposizione sullo stack l’indirizzo. Naturalmente per una parola dichiarata constant, lo stack conterrà il valore.

5.0E0 raggio f!  ok

: pigreco 3.141592E0 ;  ok

raggio f@ pigreco 2.0E0 f* f* f. 31.41592  ok

Per  inserire un valore in una variabile, o per accedervi, si transita dallo stack e si utilizzano rispettivamente le parole ! (fetch) e @ (store) e relative varianti.

Le strutture di controllo devono essere compilate, vale a dire devono essere contenute all’interno di definizione di parole. La parola if consuma il risultato di un precedente confronto. L’esempio che segue, la ricerca del massimo fra due numeri, peraltro nativa, illustra il modo di operare di Forth, in cui ci si deve preoccupare di preservare i dati (vedi 2dup):

Ridefinizione di max Contenuto dello stack Contenuto dello stack

: max ( n n -- n)

7 9

9 7

  2dup  \ duplico per confrontare

7 9 7 9

9 7 9 7

  <

7 9 -1

9 7 0

  IF

7 9

9 7

     swap

9 7

 

  THEN

 

 

  drop

9

9

;

 

 

Si noti la parola THEN che chiude il blocco IF ... ELSE ....

Esistono le strutture CASE... ENDCASE, BEGIN ... WHILE ... REPEAT, BEGIN ... UNTIL.

Naturalmente WHILE ed UNTIL consumano un valore booleano dallo stack per decidere l’iterazione del codice. Sono presenti diverse varianti di istruzioni di ciclo:

fine inizio ?DO ... LOOP

fine inizio +DO ... incremento +LOOP

inizio FOR ... NEXT  \ non definito in ANS FORTH

Le parole i, j e k contengono il valore delle variabili che controllano i cicli.

Il trattamento delle stringhe è ovviamente macchinoso, ma si può supplire con librerie, nell’esempio sottostante è riportata l’implementazione di len, left, right e mid.

\ GForth 0.5.0, Copyright (C) 1995-2000 Free Software Foundation, Inc.

: lacet ( -- addr n) s" Condor Informatique Turin" ;

: left { ind lli lle -- ind lle} \ lle quantità da estrarre

  ind

  lle 0 <

  IF lli EXIT                 \ test per < 0

  ENDIF

  lli lle < IF  lli          \ test per lle superiore

  ELSE lle

  ENDIF

;

: right ( addr n1 n2 -- addr n2) \ n2 ll parte da estrarre

  dup

  0 < IF drop EXIT      \ test per < 0

  ENDIF

  2dup

  < IF drop              \ test per ll superiore

  ELSE

    tuck                 ( n1 n2 -- n2 n1 n2)

    -                    \ offset nuova stringa

    rot + swap

  ENDIF

;

: mid ( addr n1 n2 n3 -- addr n3) \ n1 ll orig. n2 offset (primo = 1) n3 lungo

\ nessun controllo!

  2swap drop

  rot + 1 -

  swap

  exit 

;

cr lacet type

Condor Informatique Turin ok

lacet 10 left type Condor Inf ok

lacet 10 right type ique Turin ok

lacet 5 10 mid type or Informa ok

Figura 2 -6

Qui a destra un esempio di utilizzo delle funzioni stringa.

La programmazione in Forth può essere divertente e frustrante, in ogni caso inizialmente ardua. Un parziale aiuto è offerto dalla struttura { var1 var2 ... -- ... } che è volutamente analoga al commento convenzione utilizzato per la descrizione dello stack nelle funzioni. In essa le parole var1 var2 ... diventano variabili locali in cui è posto il valore prelevato dallo stack e quanto segue -- fino a }è trattato come commento (vedi la funzione left di Figura 2-6 ).

Un altro aiuto sono le librerie disponibili, fra le quali librerie per introdurre gli oggetti, le strutture record e gli array.

L’esempio che segue analizza, simulando un automa a stati finiti, una semplice espressione regolare .

\ GForth 0.5.0, Copyright (C) 1995-2000 Free Software Foundation, Inc.

include stringhe.fs

struct

  cell% field quanti         \ indicatore quantificatore 1 0 (?) -1 (*)

  cell% field ncrt           \ n.ro caratteri da confrontare

  cell% field puntatore      \ indirizzo carattere da puntare

end-struct controllo

 

create pattern 20 chars allot \ stringa da esaminare

variable memInd

variable memNum

variable llinput

create crt 1 chars allot     \ contiene il carattere esaminato

create dep 1 chars allot     \ comodo per un carattere

create azione 1 chars allot  \ contiene il tipo di carattere

variable ind                

variable ckItem

controllo 100 * %allot ind ! \ spazio x struttura, indirizzo in ind

: stato s" 0" ;

 

: ASF ( -- addr n) ( automa a stati finiti)

  \ I stato attuale, II  carattere in esame ( o tipo), III stato successivo

  \ IV  azione: e escape, c carattere, q quantificatore, d cifre

  s" 0\2e 0.1c 1q3q 1\2e 1.1c 2d1d 2q1q 3\2e 3.1c" ;

: ++ ( addr --) dup @ 1+ swap ! ;        \ var+1

: -- ( addr --) dup @ 1- swap ! ;        \ var-1

: digit ( -- addr n) s" 0123456789" ;

 

: push { addr ll n -- }                  \ n quantificatore caratteri

  ckItem @ controllo %size * ind @ + dup dup   \ per dopo

  n swap !                               \ memorizzo ll caratteri

  4 + ll swap !                          \ memorizzo contatore

  8 + addr swap !                        \ memorizzo indirizzo caratteri

  ckItem ++

;

 

: pop ( -- addr )

  ckItem --                       

  ckItem @ controllo %size * ind @ +     \ ritorno indirizzo

;

: CercaStato { crt -- nuovostato }

  asf LEN 0 +DO                          \ scan automa a stati finiti

     asf drop i + 1+ dep 1 cmove         \ mem crt automa

     stato asf drop i + 1 compare 0= IF  \ stato da macchina

        dep 1 crt 1 compare 0= IF        \ carattere uguale o .

           asf drop i + UNLOOP EXIT ENDIF    

        dep 1 s" ." compare 0= IF

           asf drop i + UNLOOP EXIT ENDIF

        dep 1 s" q" compare 0= IF

           s" *+?" crt 1 search IF       \ controllo se quantificatore

              2drop asf drop i + UNLOOP EXIT

           ELSE

              2drop

           ENDIF

        ENDIF

     ENDIF

  5 +LOOP

  -1 

;

: scanner { espreg ll -- }

  0 ckItem !      

  s" 0" drop stato cmove           \ stato iniziale

  ll 0 ?DO

     espreg i + crt 1 cmove        \ memorizzo carattere

     cr s" Da stato " type stato type s"  con " type crt 1 type

     crt CercaStato

     dup 0< IF

       drop cr espreg ll type s"  espressione non regolare " type

       UNLOOP EXIT

     ENDIF

     dup                           \ indirizzo transizione

     2 + stato cmove               \ nuovo stato

     s"  stato finale " type stato type

     3 + azione 1 cmove            \ memorizzo azione

     azione 1 s" c" compare 0= IF

       espreg i + 1 1 push ENDIF

     azione 1 s" d" compare 0= IF

       digit 1 push ENDIF

     azione 1 s" q" compare 0= IF

       pop dup 8 + @ memInd !      \ estraggo carattere precedente

       4 + @ memNum !             \ estraggo carattere precedente

       crt 1 s" *" compare 0= IF        

         memInd @ memNum @ -1 push ENDIF

       crt 1 s" +" compare 0= IF

         memInd @ memNum @ 1 push memInd @ memNum @ -1 push ENDIF

       crt 1 s" ?" compare 0= IF

         memInd @ memNum @ 0 push ENDIF

     ENDIF

  LOOP

  ckItem @ 0 ?DO

    cr

    ind @ i controllo %size * + @ .

    ind @ i controllo %size * + 4 + @ dup .

    ind @ i controllo %size * + 8 + @ swap type

  LOOP

  cr

;

: main ( --)

  cr s" introdurre stringa da controllare " type pattern 20 accept

  pattern swap

  scanner

;

main

Un esempio di esecuzione del programma:

C:\CONDOR\forth\gforth.050>gforth regexp.fs

introdurre stringa da controllare -?\d*.?\d+
Da stato 0 con - stato finale 1
Da stato 1 con ? stato finale 3
Da stato 3 con \ stato finale 2
Da stato 2 con d stato finale 1
Da stato 1 con * stato finale 3
Da stato 3 con . stato finale 1
Da stato 1 con ? stato finale 3
Da stato 3 con \ stato finale 2
Da stato 2 con d stato finale 1
Da stato 1 con + stato finale 3
0 1 -
-1 10 0123456789
0 1 .
1 10 0123456789
-1 10 0123456789
main
introdurre stringa da controllare a+b*c?
Da stato 0 con a stato finale 1
Da stato 1 con + stato finale 3
Da stato 3 con b stato finale 1
Da stato 1 con * stato finale 3
Da stato 3 con c stato finale 1
Da stato 1 con ? stato finale 3
1 1 a
-1 1 a
-1 1 b
0 1 c
 ok

2.28.1             I vari FORTH

 51forth Scott Gehmlich per 8051.

BigForth dal TurboForth

Cforth

CFORTH83 Bradley Forthware. Scritto in C per Atari.

Cyrano Opto-22. Forth per un controllore embedded.

E-Forth 1993 Lennart Benschop interprete per Motorla 6809

Eforth Un sistema prodotto da Ting per portare Forth su sistemi diversi.

EmFORTH Silicon Composers e altri. Per microprocessori dedicati: Novix Forth, Harris e SC-32 Forth.

F-PC T. Zimmer

F68K Joerg Plewe. Un Forth portabile per sistemi basati su Motorola 680x0: Atari ST e TT, Amiga, Sinclair QL e OS9.

F83 o Forth-83 1983 Henry Laxen e Michael Perry

FORTH 79

FORTH 83

Fortmacs Bradley Forthware. Scritto in C per MacIntosh.

FIG-Forth 1978. FIG (Forth Interest Group) introdusse il concetto di “FIG Forth Model” un sistema pubblicio disponibile per essere facilmente implementabile su diverse architetture. Inizialmente su 6502 e poi su su Intel 8080, DEC PDP-11, TI 9900, 6502, PACE e Motorola 6800.

LMI-FORTH (versione 3.2).

kForth per WINDOWS e BEOS.

MacForth 1984. MacIntosh.

microFort 1976 per CDP 1802, 8080 Z80 e 68000.

MMSFORTH 1979 Miller Microcomputer Service. TRS-80

MOPS Michael Hore , FORTH Object Oriented derivato da Yerk. Per l’ambiente Apple Macintosh; la versione 5.3 è di marzo 2003.

MultiForth 1980 . Motorola 689000

MVP-FORTH

Neon 1984 Charles Duff, FORTH Object Oriented. Versione commercializzata da Kriya Systems, Inc.

PolyForth Forth Inc. Multiuser e supporto DB per PC.

SunForth Bradley Forthware. Scritto in C per Sun. Utilizzato per il boot delle macchine SUN (Open Boot).

TILE Forth Mikael Patel versione attuale version: 2.1 novembre 1991. Interprete in C per Unix aderente allo standard Forth83 e dotato di molte librerie.

TurboForth

UltraForth è WolksForth per Commodere 64

Yerk fine anni 80 Bob Loewenstein, FORTH Object Oriented derivato da Neon. Sviluppato per la piattaforma Apple Macintosh. La versione 3.68 è del 1996.

WolksForth per Commodere 64, su Atari ST nel 1976.


[1] Intel 8086, Nova, RCA 1802, Motorola 68000 e 6809, Alpha Micro, VAX, e Data General Eclipse.

[2] In grassetto la risposta dell’interprete.