1.1        BCPL

BCPL (Basic CPL) è opera di Martin Richards nel 1966 ideato nella sua tesi di dottorato; la prima implementazione è della primavera del 1967 al MIT.

Inizialmente BCPL era uno strumento per produrre il compilatore CPL, ma fu poi utilizzato per scrivere dei sistemi operativi quali Tripos e Amiga Kickstart. BCPL è stato il primo linguaggio a generare un codice per una macchina virtuale (O-Code).

Nel 1979 era disponibile su oltre 25 sistemi diversi.

Il linguaggio, che inizialmente aveva solo il tipo di dato corrispondente alla voce del computer, ora tratta numeri interi, valori booleani, stringhe di caratteri e vettori.

E’ presente una certa ridondanza sintattica nelle espressioni condizionali: assegnazione condizionale, (condizione -> ...), if (IF ... DO ...) e if then else (TEST ... THEN ... ELSE ...) come si può vedere nell’esempio sottostante.

/* BCPL (14 Aug 2001) */

GET "libhdr"

LET start() = VALOF

{     LET numrnd = ?

      LET res = ?

      numrnd := randno(100)

      res := "pari"

      IF numrnd REM 2 = 1 res := "dispari"

      writef("*n%i %c %s", numrnd, 32, res)

      TEST numrnd REM 2 = 1 THEN res := "dispari"

      ELSE res := "pari"

      writef("*n%i %c %s", numrnd, 32, res)

      res := numrnd REM 2 = 1 -> "dispari", "pari"

      writef("*n%i %c %s*n", numrnd, 32, res)

      RESULTIS 0

}

C’è differenza fra i simboli per l’assegnazione (variabile := espressione), e l’assegnazione al momento della dichiarazione, obbligatoria, delle variabili: LET variabile = valore. Un altro esempio di varianza sintattica riguarda la dichiarazione di vettori:

·         per i vettori locali: LET variabile = VEC numero_elementi

·         con assegnazione di valori: TABLE  valore1, valore2, ...

·         per i vettori globali, mediante assegnazione di memoria: variabile := getvec(1000)

L’ambito delle variabile e delle costanti può essere globale, tramite le dichiarazioni GLOBAL, MANIFEST e STATIC, o locale con LET; locale è anche la variabile, da non dichirare preventivamente, che controlla l’istruzione FOR; AND al posto di LET estende l’ambito delle variabili del blocco precedente, al blocco che ha origine dall’AND.

Il linguaggio è dotato, abbondantemente, di istruzione per l’iterazione (WHILE, UNTIL, REPEAT, REPEATWHILE, REPEATUNTIL, LOOP, BREAK, FOR) e SWITCHON un goto calcolato e non un vero e proprio CASE. Il valore opzionale di incremento del ciclo FOR deve essere determinato al momento della compilazione, quindi non può essere una variabile.

I programmi necessitano di un modulo principale di nome start, dichiarato o come funzione: LET start() = VALOF, o come procedura: LET start() BE .... L'istruzione, opzionale, di uscita da una procedura è return; RESULTIS valore, è l'uscita da una funzione.

Alcune funzioni, in genere scarne, sono disponibili in librerie: accesso ai file, gestione a basso livello del multitasking (coroutines), supporto di un embrione di programmazione OO, limitato trattamento delle stringhe. Viceversa è esteso il supporto dell’interazione con l’interprete e il sistema operativo, tramite la funzione sys(cod_operazione, parametro(i))e la gestione di stringhe di comandi.

E’ possibile estrarre bits all’interno di aree di memoria, questa caratteristica, con le coroutines e le funzioni sys() giustifica l’utilizzo che è stato fatto di BCPL per lo sviluppo di sistemi operativi.

Il codice qui riprodotto è un’interprete (quasi completo) di BEFUNGE (v. BEFUNGE par. 2.34.2 ).

/* BCPL (14 Aug 2001) */

// Interprete Befunge

GET "libhdr"

GLOBAL {stack: ug; memory}

STATIC {dimpgm = 0; dep = 0;                   // comodo

        swstring = 0;                   

        ckstack = 0; Mx = 0; My = 0}

LET start() = VALOF

  {LET fp = ?

   LET argv = VEC 50

   LET cicla = 1                               // variabile di controllo ciclo

   stack := getvec(1000)                       // alloca stack

   memory := getvec(2000)                      // alloca memoria

   TEST rdargs("PROGRAMMA/A", argv, 50) THEN

      {dimpgm := argv!0%0

      FOR i = 1 TO dimpgm DO memory%(i-1) := argv!0%i}

   ELSE

      {fp := sys(14,"source.bf")                     // open file

      dimpgm := sys(12,fp,memory,2000)               // leggo programma Befunge

      sys(16,fp)}                                    // close file

   writef("*n dimpgm:%i *n",dimpgm)

   WHILE cicla DO

      {dep := INTERP(memory%(Mx + 80*My))

       SWITCHON dep INTO

        {CASE 1: Mx := Mx + 1; ENDCASE

         CASE 2: Mx := Mx - 1; ENDCASE

         CASE 3: My := My + 1; ENDCASE

         CASE 4: My := My - 1; ENDCASE

         CASE 5: Mx := Mx + 2; ENDCASE}

       IF dep = 0 BREAK

       IF Mx + 80*My > dimpgm DO {writef("*nFuori memoria!*n"); cicla:=0}

      }

      RESULTIS 0

}

 

AND INTERP(k) = VALOF

      {LET rc,rc2 = 0,?

      IF k = '*"' DO {swstring := swstring NEQV -1; RESULTIS 1}  // NEQV = XOR or esclusivo

      IF swstring DO {PUSH(k); RESULTIS 1}

       SWITCHON k INTO

        {

          CASE '>': RESULTIS 1                             // muovi a destra

          CASE '<': RESULTIS 2                             // muovi a sinistra

          CASE '^': RESULTIS 4                             // muovi su

          CASE 'v': RESULTIS 3                             // muovi giu

          CASE '+': PUSH(POP() + POP()); RESULTIS 1        // somma

          CASE '-': PUSH(POP() - POP()); RESULTIS 1        // sottrai

          CASE '**': PUSH(POP() * POP()); RESULTIS 1       // moltiplica

          CASE '/': PUSH(POP() / POP()); RESULTIS 1        // dividi

          CASE '.': writef("%i %c",POP(),32); RESULTIS 1   // stampa

          CASE '0':CASE '1':CASE '2':CASE '3':CASE '4':

          CASE '5':CASE '6':CASE '7':CASE '8':CASE '9':

             PUSH(k & #xF)

             RESULTIS 1

          CASE '|': rc := POP() ~= 0 ->  4, 3; RESULTIS rc  // vertical if

          CASE '#': RESULTIS 5                             // skip 1 if POP diverso da 0

          CASE '$': POP(); RESULTIS 1                      // elimina TOP

          CASE '\': rc := POP()                            // SWAP

                    rc2 := POP()

                    PUSH(rc)

                    PUSH(rc2)

                    RESULTIS 1

          CASE ':': PUSH(TOP()); RESULTIS 1                // duplica

          CASE '&':                                        // immissione numero

            writef("Immetti numero ")

            PUSH(readn())

            RESULTIS 1 

          CASE 'g': rc := 80*POP() + POP()

                    PUSH(memory%rc); RESULTIS 1 // get

          CASE 'p': rc := 80*POP() + POP()

                    memory%rc:= POP()

                    RESULTIS 1                               // put

          CASE '`': TEST POP() > POP() THEN PUSH(1) ELSE PUSH(0)

                    RESULTIS 1

          CASE '!': rc2:=POP();rc:= rc2 = 0 -> 1,0; PUSH(rc)

                    RESULTIS 1                                // not 

          CASE '@':

            writef("*nMx: %i %c My: %i %c HStack: %i",Mx,32, My,32,ckstack)

            writef("*nFine*n")

            RESULTIS 0                                     // fine

      }

      RESULTIS 1

}

AND PUSH(x) BE

      {ckstack := ckstack + 1

      stack!ckstack := x}

AND POP() = VALOF

      {IF ckstack = 0 DO RESULTIS 0                  // stack vuoto ritorna 0

      ckstack := ckstack - 1

      RESULTIS stack!(ckstack+1)}

AND TOP() = stack!ckstack                            // legge stack

 

1.1.1            VSPL

Linguaggio didattico

VSPL (Very Simple Programming Language) è uno strumento didattico (Martin Richard 2000) derivato da BCPL, per dimostrare come si realizza un compilatore. Il linguaggio genera un'immagine del programma come codice intermedio. L'introspezione avviene tramite alcune funzioni sys(n, param) che interagiscono con l'interprete, e l'esecuzione con opzioni per ottenere traccia, codice intermedio, analisi sintattica e lessicale.

Un esempio:  

static valori[10], dummy

let fibon(num) be

{ let a = 0;  // primo numero

  let b = 1;  // secondo numero

  while num do

   {

     printf("%d ",a);

     valori[num] := a;

     b := b + a;

     a := b - a;

     num := num -1

  }

}

 

let start() be

{ let finoa = 10;

  printf("I primi %d numeri di Fibonacci:\n", finoa);

  fibon(finoa);

  for i=0 to 6 do {

   printf("\nfattoriale di %d e' %d", valori[10-i],fatt(valori[10-i]))}

  }

 

let fatt(num) = valof

{ if num = 0 do resultis 1;

resultis num * fatt(num-1)

}

Il risultato è:

VSPL (29 May 2000) C version

Program size: 160   Data size: 33

I primi 10 numeri di Fibonacci:

0 1 1 2 3 5 8 13 21 34

fattoriale di 0 e' 1

fattoriale di 1 e' 1

fattoriale di 1 e' 1

fattoriale di 2 e' 2

fattoriale di 3 e' 6

fattoriale di 5 e' 120

fattoriale di 8 e' 40320

Instructions executed: 764