T3X

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