2.45        MUMPS (M)

MUMPS (Massachusetts General Hospital Utility Multi-Programming System) fu sviluppato su un PDP-7 presso il Massachusetts General Hospital di Boston a cura di Octo Barnett negli anni 1966-1967, il nome originale era FileMan.

Lo scopo di MUMPS era di disporre di un sistema facile da usare per memorizzare informazioni cliniche. Successivamente fu trasportato su PDP-15, PDP-8, Data General Nova, ecc… MUMPS si diffuse negli ambienti medici con un proliferare di versioni particolari, tant’è che nel 1972 un gruppo di utilizzatori creò un comitato per la sua standardizzazione. La standardizzazione ANSI è del 1977; nel 1990 il comitato cambiò il nome del linguaggio in M. MUMPS è ancora largamente utilizzato.

MUMPS nasce come ambiente di lavoro multiutente su calcolatori con sistemi operativi embrionali, per cui il linguaggio incorpora funzioni che saranno proprie dei sistemi operativi successivi, quali il multitasking. MUMPS nasce come DBMS, in quanto implementa un proprio Data Base con la tecnologia Btree e salva i dati a fine sessione su di un file.

La sintassi di MUMPS risente delle limitazioni di memoria e di cpu dell’epoca in cui fu sviluppato, la maggior parte dei comandi può essere espressa con la sola iniziale, non sono ammessi spazi fra operandi ed operatori.

MUMPS tratta solamente stringhe di caratteri, interpretate in funzione delle operazioni che su di esse si effettuano. Esiste una sola struttura di dati complessa, l’array multidimensionale, i cui indici possono essere una qualsiasi stringa di caratteri o variabile (il segno ^ indica che la variabile fa parte del Global Array, l’insieme di dati salvati a fine sessione nel DataBase):

       SET ^anag("10")="Rossi"

       SET ^anag("03")="Verdi"

       SET ^anag("01")="Bianchi"

       SET ^anag("07")="Neri"

       SET ^titolo(^anag("10"))="doc"

       SET ^indirizzo(^anag("10"))="Via Roma"

       SET vrb=-1

       FOR  DO  write " ",^anag(vrb),!

       . SET vrb=$NEXT(^anag(vrb))

       . if vrb<0 BREAK

       . WRITE vrb

01 Bianchi

03 Verdi

07 Neri

10 Rossi

Figura 25

Nella Figura 25 a destra il risultato del frammento di programma di sinistra.

La memorizzazione dei dati utilizzando come indice una variabile, permette di scrivere istruzioni come la seguente:

write ^titolo("Rossi")," Rossi ",^indirizzo("Rossi"),!

doc Rossi Via Roma

Gli operatori relazionali sono a tutti gli effetti operatori numerici, il risultato del confronto è indicato nella variabile $TEST: 1 se vero, altrimenti 0; tale variabile è utilizzata anche per indicare la fine di un file in lettura (0); in conseguenza di ciò l’istruzione ELSE è indipendente dall’istruzione IF ed equivale a IF $TEST’=1 (' è l’operatore di negazione). Le istruzioni che dipendono da IF o da ELSE o dall’istruzione iterativa FOR o sono sulla stessa riga o sono sulle righe immediatamente successive e queste devono iniziare con uno o più punti; la presenza di più punti indica dei blocchi nidificati.

Per le iterazioni è solamente disponibile l’istruzione FOR, con una sintassi versatile e bizzarra; una delle strutture di FOR è: var=i:p:f DO ... dove i, p e f sono rispettivamente il valore iniziale di var, l’incremento ed il valore finale. var=i:p:f può mancare del tutto o in parte. Alla variabile di ciclo possono anche essere imposti dei valori espliciti:  var=i,val1,val2,... o un misto di valori iterativi ed espliciti.

Un’altra bizzarria è l’esecuzione delle istruzioni controllate da FOR (v. esempio di Figura 25 ), vale a dire l’istruzione sulla stessa riga del comando è eseguita ad ogni ciclo, ma dopo le istruzione del blocco.

Il trattamento delle stringhe di caratteri si basa sull’operatore di concatenamento _ (underscore), alcuni operatori relazionali: e delle funzioni. Gli operatori relazionali sono =, [, ], e ?; [ determina se una stringa è contenuta in un altra, ? è un operatore di pattern matching (esiste anche una libreria che implementa il trattamento delle espressioni regolari alla PERL), alcune delle funzioni per trattare le stringhe sono: $EXTRACT, $PIECE, $FIND, $LEN.

Utile la possibilità di scrivere istruzioni condizionate nella forma comando:condizione operandi, ad esempio: set:(a/2)=(a\2) a=a+1, in cui la variabile a è resa dispari; nel confronto si è utilizzata la divisione frazionale / e quella intera \.

Anche nella versione compilata il linguaggio ha conservato la possibilità di interpretare tramite l’operatore @ (write @"2+3",!) e l’istruzione $XECUTE. Non è particolarmente evoluto l’accesso ai file, ed è anche un po’ macchinoso, dovendo indicare tramite il comando USE quale file si sta trattando.

MUMPS prevede funzioni dell’operatore richiamate tramite DO nomefunzione, e terminate dal comando QUIT con un eventuale risultato di ritorno, tuttavia perché agiscano come funzioni, devono essere richiamate prefissandole con $$, il prefisso ^^ indica le funzioni esterne al programma.

MUMPS è un linguaggio longevo ed adattabile: convenzionalmente le funzioni $Z... sono a disposizione degli implementatori di MUMPS, dalla versione 4 il data base di default è il Berkeley Data Base, è possibile utilizzarlo come programma CGI in HTML, tramite il comando SQL interagisce con i Data Base in SQL.

Il sorgente sottostante è un compilatore di un semplice linguaggio che produce un programma BEFUNGE (v. BEFUNGE par. 2.38.2 )

#        The Mumps Compiler Build 5.04 Aug 13 2002

         ZMAIN

         SET dimr=80                         // dimensione righe befunge

         SET mem("a")=$zp("v",dimr)          // area salti in avanti (riga 0)

         SET mem("pgm")=""                   // area programma befunge (riga 1)

         SET mem("i")=$zp(" ",dimr)          // area salti indietro (riga 2)

         SET mem("mem")=$zp(" ",dimr)        // area dati (riga 3)

         SET ckmem=0                         // contatore memoria

         SET quote=$CHAR(34)                 // "

         OPEN 2:"befunge.bf,old"

         WRITE "Macro generatore per Befunge",!

         FOR  DO 

         . USE 2 READ sk

         . IF '$test BREAK                   //  EOF

         . DO INTERP(sk)

         . SET mem("pgm")=mem("pgm")_pgm

         . USE 5 WRITE sk,?30,pgm,!          // ? tabulatore

         CLOSE 2

         USE 5

         WRITE "Fine Macro generazione per Befunge",!

         WRITE mem("a")                  // area salti indietro

         SET mem("pgm")=$zp(mem("pgm"),dimr)

         WRITE mem("pgm")                // area programma befunge

         WRITE mem("i")                  // area salti avanti

         WRITE mem("mem")                // area dati

         OPEN 3:"source.bf,new" USE 3

         WRITE mem("a")                  // area salti indietro

         SET mem("pgm")=$zp(mem("pgm"),dimr)

         WRITE mem("pgm")                // area programma befunge

         WRITE mem("i")                  // area salti avanti

         WRITE mem("mem"),!              // area dati

         CLOSE 3

         HALT

INTERP(sk) SET pgm=""

         if $$^^perlmatch(sk,"^REM.*") QUIT  // Commento

         if $$^^perlmatch(sk,"^LABEL[ ]+([A-Z]+)") DO LABEL QUIT

         set MatchIstr3="^[ ]+([A-Z]+)[ ]+([A-Z,0-9]+)+[ ]+([A-Z,0-9]+)"

         if $$^^perlmatch(sk,MatchIstr3) DO GENISTR QUIT

         set MatchIstr2="^[ ]+([A-Z]+)[ ]+([A-Z,0-9]+)"

         if $$^^perlmatch(sk,MatchIstr2) DO GENISTR QUIT

         set MatchIstr="^[ ]+([A-Z]+)"

         if $$^^perlmatch(sk,MatchIstr) DO GENISTR QUIT

         SET mem("pgm")=mem("pgm")_sk      // codice nativo

         QUIT

GENISTR  IF $1="MOVE" GOTO MOVEADD QUIT

         IF $1="ADD" GOTO MOVEADD QUIT

         IF $1="COMPARE" GOTO MOVEADD QUIT

         IF $1="PRINT" GOTO STAMPA QUIT

         IF $1="ASK" GOTO CHIEDI QUIT

         IF $1="IFNONEQUAL" GOTO VA QUIT

         IF $1="END" SET pgm="@"

         QUIT

LABEL    SET cmd="SET "_$1_"=1+$LEN(mem("_quote_"pgm"_quote_"))"

         XECUTE cmd

         QUIT

CHIEDI   SET cmd="SET rc=$DATA("_$2_")"      // ASK

         XECUTE cmd

         IF rc=0 DO GENVRB($2)               // assegno memoria a nuova variabile  

         SET cmd="SET vrb="_$2               // indirizzo variabile

         XECUTE cmd                         

         SET pgm="&"_vrb_"3p"

         QUIT

MOVEADD  SET cmd="SET rc=$DATA("_$3_")"      // MOVE ADD COMPARE

         XECUTE cmd

         IF rc=0 DO GENVRB($3)              // assegno memoria a nuova variabile  

         SET cmd="SET vrb="_$3

         XECUTE cmd                          // indirizzo variabile

         SET dest=vrb_"3"                    // seconda variabile

         IF $zu($2)=1 SET pgm=$2             // I operando è una costante

         ELSE SET cmd="SET vrb="_$2 XECUTE cmd SET pgm=pgm_vrb_"3g"

         IF $1="MOVE" DO

         . SET pgm=pgm_dest_"p"

         ELSE DO

         . IF $1="ADD" DO 

         .. SET pgm=pgm_dest_"g+"_dest_"p"

         . ELSE DO

         .. SET pgm=pgm_dest_"g-!"            // COMPARE

         QUIT

STAMPA   SET cmd="SET vrb="_$2

         XECUTE cmd                          // indirizzo variabile

         SET pgm=vrb_"3g."

         QUIT

VA       SET cmd="SET rc="_$3                // indirizzo del salto

         XECUTE cmd

         SET pgm="|"                         // vertical IF

         SET vrb="^"

         SET lpgm=$LEN(mem("pgm"))

         FOR i=1:1:lpgm-rc+1 SET vrb=vrb_"<"

         SET mem("i")=$$INSERT(mem("i"),vrb,rc)

         SET mem("a")=$$INSERT(mem("a"),">v",lpgm+1)

         QUIT

GENVRB(vrb)   SET ckmem=ckmem+1

         SET cmd="set "_vrb_"=ckmem"

         XECUTE cmd

         QUIT

INSERT(str,istr,dv)

         SET str=$EXTRACT(str,1,dv-1)_istr_$EXTRACT(str,dv+$LEN(istr),$LEN(str))

         QUIT str