T3X , é opera di Nils M Holm nella metà degli anni 90, inizialmente il nome era T .
Lo scopo di T3X è di essere uno strumento portabile e di facile utilizzo, per lo studio e la prototipazione di algoritmi, scopo che è ottenuto grazie ad un linguaggio semplice ed elegante il cui compilato è in linguaggio di una macchina virtuale a 16 bit (Tcode ).
Dato lo scopo T3X non è dotato di sofisticate strutture dati e varietà di tipi, tuttavia Table, un’estensione dei vettori, il supporto della ricorsione e, dalla versione 6, la gestione ad oggetti, lo rendono un valido strumento per la didattica e la ricerca.
T3X è strutturato e "quasi" senza tipi, nel senso che ad una variabile corrisponde una word della macchina che può contenere un numero intero o un indirizzo di un oggetto di una procedura o di una stringa di caratteri. Come accennato le strutture dati sono vettori, table vettori pluridimensionali e le STRUCT o strutture, tuttavia queste sono semplicemente dei vettori i cui componenti sono accessibili anche per nome:
STRUCT NumImg =
reale, img; ! Struttura Numero
immaginario
VAR n1[NumImg],
n2[NumImg]; n3[NumImg] ! creazione di
numeri immaginari
n3[0]:=n1[reale]+n2[reale]; n3[1]:=n1[img]+n2[img];
Le table sono strutture dinamiche, i cui elementi possono contenere numeri, stringhe, table, oggetti ed espressioni. Quest’ultime vanno racchiuse entro parentesi tonde e sono valutate all’atto dell’assegnazione. Il frammento di programma che segue illustra la flessibilità di queste strutture (a destra il risultato).
J:= 113;
TAB := [["+",(J+4)],["-",[[1],[2]],"Subtract"]];
U.Printf("Stringa %s\n\r",[(TAB[1][2])]);
TAB[1][2] := [99,77]; ! cambio il contenuto della cella
U.Printf("Numero %d\n\r",[(TAB[1][2][1])]);
TAB[20][1] := TAB[0][1]; ! estendo TAB con J+4
U.Printf("Estensione %d\n\r",[(TAB[20][1])]);
|
Stringa Subtract
Numero 77
Estensione 117
|
T3X amplia le sue capacità di calcolo tramite classi predefinite per accedere a file, trattare caratteri e stringhe, interfacciare il sistema operativo, gestire la memoria ed i task concorrenti, interagire con la macchina virtuale ecc. L’aritmetica di T3X è aritmetica intera con i quattro operatori e mod per il resto della divisione di due numeri. La combinazione di . (punto) con un operatore indica di effettuare l’operazione o il confronto con numeri non segnati. Gli operatori logici e booleani sono elencati nella tabella sottostante.
|
Operatori
logici |
|
|
|
|
Operatori
booleani e sui bit |
|
|
|||||||
AND |
OR |
NOT |
AND |
OR |
XOR |
NOT |
Shift |
Shift |
|||||||
/\ |
\/ |
\ |
& |
| |
^ |
~ |
<< |
>> |
|||||||
I delimitatori di blocchi di istruzione sono DO ed END, utilizzabili sia per le procedure o le dichiarazioni di classi, sia per le istruzioni condizionali o di ciclo; essi sono necessari solo se il blocco contiene più di un’istruzione.
Le istruzioni condizionali
sono due:
IF condizione comando e
IE
condizione comando ELSE comando
La scelta di differenziare i due comandi nasce dal voler evitare l’ambiguità dell’istruzione:
IF condizione IF condizione ELSE comando
Per i cicli sono disponibili FOR e WHILE, da questi si può uscire prematuramente tramite il comado LEAVE.
Quadrato(x) RETURN x * x;
IND := @Quadrato; J := CALL IND(17);
J := Quadrato(17);
|
Non c’è distinzione fra procedure e funzioni, implicitamente un’uscita da subroutine, senza l’istruzione RETURN espressione;, restituisce 0; le funzioni possono essere richiamate anche indirettamente, tramite CALL variabile(...), dove variabile contiene l’indirizzo della funzione ottenuto tramite l’operatore @ (variabile := @nomefunzione).
L’esempio che segue è un’embrionale interprete Forth: .
! T3X -- A Compiler for the Minimum Procedural Language T3X V. 8
! Copyright (C) 1996-2003 Nils M Holm. All rights reserved.
CLASS stack()
VAR list[1000], J;
Start() list[0] := 0; ! house keeping
PUBLIC push(x) DO
list[0] := list[0] + 1;
list[list[0]] := x;
END
PUBLIC pop() DO
list[0] := list[0] - 1;
RETURN list[list[0] + 1];
END
PUBLIC GetVal(x) RETURN list[x];
END
DECL Interpreter(1); ! for the compiler is one step
CONST EndList = -1, FUNCTION = 2, FORTHPROC = 5;
VAR Dict; ! Dictionary table
VAR buffer::80; ! general work memory
MODULE main(string,char,ttyctl,stack);
OBJECT STR[STRING], CHR[CHAR], TTY[TTYCTL], STK[STACK];
Elab(bf) DO ! search dictionary and execute word or push number
VAR J, INDPROC; FOR (J=0, 100, 1) DO IF (Dict[J][1]= EndList) LEAVE; ! -1 exit
IF (STR.COMP(bf,Dict[J][0]) = 0) DO ! word found
IE (Dict[J][1] <= FUNCTION) DO
IF (stk.GetVal(0) < Dict[J][1]) DO
Elab("uflow");
RETURN;
END
INDPROC := Dict[J][3];
IE (Dict[J][2] = 0 ) CALL INDPROC(); !word return value
ELSE stk.PUSH(CALL INDPROC(stk.POP(),stk.POP()));
END
ELSE interpreter(Dict[J][3]); ! Forth source
RETURN;
END
END
stk.PUSH(str.STRTONUM(bf,10,0)); ! maybe a number
END
Type() tty.WRITES(str.FORMAT(buffer," %D",[(stk.POP())]));
Swap() DO VAR a,b; a:= stk.POP(); b := stk.POP(); stk.PUSH(a); stk.PUSH(b); END
Dup() stk.PUSH(stk.GetVal(stk.GetVal(0)));
Drop() stk.POP();
Sub(op1,op2) RETURN op2 - op1;
Mult(op1,op2) RETURN op1 * op2;
Divide(op1,op2) RETURN op2 / op1;
inspect() DO
VAR J;
FOR (J=1,stk.GetVal(0)+1,1) tty.WRITES(str.FORMAT(buffer," %d",[(stk.GetVal(J))]));
END
ReadLine(bfr) DO ! read line
VAR JB, k;
JB := 0;
WHILE (1) DO
k := tty.READC(); ! Read single key from TTY
IF (k = TTYCTL.K_CR) RETURN JB; ! Carriage Return
tty.WRITEC(k); ! Write single key to TTY
bfr::JB := k; IE (K \= TTYCTL.K_BKSP) JB := JB + 1;
ELSE JB := JB -1; ! back space END
END
info() DO
tty.CLEAR(); ! clear screen
tty.WRITES("Condor Informatique FORTH-T3X - 1 May 2004\r\n");
tty.WRITES("type bye for exit, help for word's list\r\n\n");
END
undflow() tty.WRITES(" Underflow!");
help() DO
VAR J;
FOR (J=0, 100, 1) DO
tty.WRITES(str.FORMAT(buffer,"\n\r%s %s",[(Dict[J][0],Dict[J][4])]));
IF (Dict[J][1]= EndList) LEAVE; ! -1 exit
END
END
interpreter(bfr) DO
VAR LL, k, JB, word::20;
LL := 0;
FOR (JB = 0, str.LENGTH(bfr) + 1, 1) DO
k := bfr::JB; IE (chr.SPACE(k) \/ k = 0) DO word::LL := 0; ! string terminator
IF (str.COMP(word, str.FORMAT(buffer,"%s",["bye"])) = 0) RETURN 0; ! the end
elab(word);
LL := 0;
END
ELSE DO
word::LL := k;
LL := LL + 1;
END
END
RETURN -1;
END
DO
VAR rdBuffer::80;
! dictionary word, type (0,1,2 arity), 5 Forth source, -1 end table),
! Return values (1,0), value, description
Dict := [["+",5,1,"0 swap - -","Add"],["-",2,1,(@sub),"Subtract"],
["*",2,1,(@mult),"Multiply"],["/",2,1,(@divide),"Divide"],
[".",1,0,(@type),"Type"],["dup",1,0,(@dup),"Duplicate"],
["swap",2,0,(@swap),"Swap"],["drop",1,0,(@drop),"Eliminate"],
[".s",0,0,(@inspect),"See stack"],["info",0,0,(@info),"Info"],
["help",0,0,(@help),"Words list"],["uflow",0,0,(@undflow),"Underflow"],
["",-1,0,0,""]];
str.FORMAT(rdBuffer,"info","");
WHILE (interpreter(rdBuffer)) DO
tty.WRITES(" Yes\n\r");
rdBuffer::ReadLine(rdBuffer) := 0; ! string terminator
END
tty.WRITES("\r\nSee you later");
END
|