Icon

manipolazione di testi

Icon è un sofisticato linguaggio per il trattamento di stringhe, ed è considerato il successore di SNOBOL. Uno dei suoi ideatori, Ralph Griswold, partecipò alla realizzazione di SNOBOL e successivamente, nel 1977, allo sviluppo di SL 5 (SNOBOL Language 5). SL 5 suppliva ad alcune carenze sintattiche di SNOBOL, tuttavia nel 1977 si pensò ad un linguaggio che ereditasse il meglio di SNOBOL4 e di SL 5, linguaggio con un diverso sistema di pattern matching e basato sul concetto di generatore, vale a dire un meccanismo per produrre tutti i risultati di un’espressione, anzi ogni espressione tramite il comando every diventa un generatore; tramite i generatori si possono costruire le strutture LOOP (every i:= n to m do ...) , i risultati di una espressione, forniti interattivamente, permettono l’elaborazione del testo durante il riconoscimento. Un altro generatore, utilizzabile per stringhe e liste è l’operatore unario!, che fornisce i caratteri di una stringa o gli elementi di una lista.

I tipi di dato di Icon sono numeri, caratteri, stringhe, procedure e i tipi di dati aggregati insiemi (set), liste, matrici associative, strutture o record, i cui componenti possono essere di qualsiasi tipo.

In Icon il risultato di una relazione non è vero o falso, ma rispettivamente il valore del relato più a destra o il fallimento della relazione, così l’istruzione:

every write(6 < (1 to 10 by 2))

produce:

 7 9

poiché 6 è confrontato, per effetto dell’iteratore every, con 1 3 5 7 9 e l’istruzione writes è eseguita, solo per i valori che soddisfano la relazione; poiché in Icon tutto è espressione, è lecito un programma del genere (| è il simbolo per concatenare espressioni):

procedure main()

every n:= 2 | ?100 | n+1 | n+26 do

      write(n,if (d:= primo(n)) = n then " primo"

                                    else " divisibile per " || d)

end

procedure primo(n)

 (n % 2) = 0 & return 2;

 every i:= (3 to n^.5 by 2) do (n % i) = 0 & return i;

 return n

end

2 primo

22 divisibile per 2

23 primo

49 divisibile per 7.0

Il cui risultato si può vedere qui accanto. Nell’esempio every esegue write(...) 4 volte: per n a cui è assegnato il valore 2, poi per n contenente un numero a caso fra 1 e 100, indi per n incrementato di 1 ed ancora di 26. write dopo aver stampato n, stampa il valore dell’espressione if. La funzione primo(n), se n è pari ritorna il valore 2, altrimenti fornisce il più piccolo divisore di n (diverso da 1 ovviamente).

Un esempio che chiarisce la potenza del meccanismo di ricerca e fallimento è il seguente frammento di codice[1] che trova tutte le triple di numeri interi, fra 1 e 20, che soddisfano il teorema di Pitagora:

fl := open("s.txt","w")

every i:=1 to 20 &

      j:=i to 20 &

      m:=j+1 to 20 &

      m*m = i*i+j*j &

      write(fl,i,char(9),j,char(9),m)

3     4     5

5     12    13

6     8     10

8     15    17

9     12    15

12    16    20

 

Alcuni simboli sono usati in contesti unari e binari, con significati del tutto o quasi non correlati:

Simbolo

simbolo espressione

espressione simbolo espressione

?

Genera numero a caso

Scansione di stringa

^

Rigenera  co-espressione

Eleva a potenza

*

Dimensione

Prodotto

/

Test per null

Quoziente

\

Test per non null

Limitazione

Le funzioni o procedure possono avere più parametri, ciò vale ad esempio anche per la funzione arcotangente atan(x,y), con y, se omesso, assunto pari ad 1, e la tangente è il rapporto fra x ed y, ciò permette di trattare il caso in cui la tangente vale ±∞.

Il comando suspend espressione trasforma una funzione in un generatore, ad ogni richiamo della funzione espressione fornirà un nuovo valore.

In Icon sono presenti dei generatori infiniti:

·         seq()       1,2,3, ...

·         | k         k,k,k, ... | è il ripetitore di concatenazione di espressioni

Fortunatamente l’operatore (binario) \ limita la generazione indefinita:

every writes(| (0|1) \ 8)  produce 01010101

Le strutture di controllo, oltre a quelle ottenibili con l’espressione n to m, sono if then [else], while, case, repeat e until.

Icon è un linguaggio per il trattamento di testi, ed è quindi naturalmente ricco di funzioni per il trattamento di stringhe, sia funzioni native sia presenti in librerie (mapstrs, strings); inoltre per facilitare le operazioni di scansione di stringhe, esistono insiemi predefiniti quali &digits, &letters, &lcase, ecc… e ad ogni variabile stringa è associato un pointer alla posizione interessata al processo di scansione. Le funzioni di scansione hanno, in genere, la struttura:

funzione(modello,stringa,posizione_iniziale,posizione_finale)

in esse possono essere omesse le posizioni iniziali e finali, se i valori di default sono accettabili. Qui di seguito un elenco di alcune funzioni di scansione:

·        MANY() verifica quanti carattere all’inizio della scansione appartengono ad un certo insieme di caratteri. 

·        UPTO() restituisce le posizioni in cui la stringa contiene caratteri appartenenti ad un certo insieme.

ANY(), MANY() e MATCH() restituiscono il puntatore al carattere successivo il match.

L’operatore ?, il cui antecedente è una stringa, pone questa come default nelle funzioni di scansione che seguono l’operatore: le istruzioni che seguono sono pertanto equivalenti:

wd := "2*sin(37-a)"

match("sin",wd,find("*",wd)+1)

wd ? match("sin",,find("*")+1)

Sono disponibili molte librerie, utilizzabili mediante il comando link nomelibreria, che offrono varie funzioni, fra cui trattamento di stringhe, funzioni grafiche e per la programmazione ad oggetti (IDOL).

Il linguaggio è giunto alla versione 9; Jicon è un generatore di classi Java. Le librerie per la grafica permettono di realizzare facilmente applicazioni grafiche interattive, è tuttavia anche disponibile uno strumento di programmazione visuale (VIB) nell’esempio che segue illustra alcune capacità grafiche di Icon, e come Icon gestisce l’interattività.

# Window Icon Version 9.3.2 December 4, 1998

link graphics

global colori

procedure main()

  colori := ["white","black","white"] # per disegno in rilevo

  Htxt := 16 # altezza testi

  bottoni := [["Fine",401, 360, 60,20,Fine],["Disegna",340, 360, 60,20, Disegna]]

  WOpen("size=500,400") | stop("impossibile aprire la finestra")

  WAttrib("fg=light grey", "linewidth=1","label=Prova eventi")

  FillRectangle()

  Fg("Black")

  Font("typewriter,bold," || Htxt)    # || op. concat. stringhe

  every itm := !bottoni do {          # generazione bottoni

     bottone(itm,"su")

     CenterString(itm[2] + itm[4] / 2, itm[3]+itm[5] / 2, itm[1])

    }

# gestione eventi    

  repeat case Event() of {

     tst := (&lpress | &lrelease): SeBottone(bottoni,tst)

     }

end

procedure SeBottone(bottoni,tst)

every itm := !bottoni do {

    if (itm[2] < &x <itm[2]+itm[4]) & (itm[3] < &y <itm[3] + itm[5]) then {

       if tst = &lpress then bottone(itm,"giu")

           else  {bottone(itm,"su"); itm[6](); }    # eseguo metodo associato

        }

}

end

procedure bottone(itm,direz)

    FgColor := WAttrib("fg")                          # colore precedente

    Fg(colori[indx:= x[find(direz,x:="1su2giu")-1]])  # contorto vero?

    DrawLine(itm[2],itm[3],itm[2]+itm[4],itm[3],itm[2]+itm[4],itm[3]+itm[5])

    Fg(colori[indx+1])

    DrawLine(itm[2]+itm[4],itm[3]+itm[5],itm[2],itm[3]+itm[5],itm[2],itm[3])

    Fg(FgColor)

end

procedure Fine()

  WriteImage("graf.gif");

  write("Fine") & exit()

end

procedure Disegna()

  rosso := ?65000; # un numero a caso fra 0 e 65000

  verde := ?65000; # un numero a caso fra 0 e 65000

  blu := ?65000;   # un numero a caso fra 0 e 65000

  Fg(rosso || "," || verde || "," || blu);

  FillCircle(?450,?300,?10);

  retun;

end

 

Il codice sorgente che segue è un traduttore, incompleto, in linguaggio CUPL (v. CUPL) di programmi IT (v. IT).

# Window Icon Version 9.3.2 December 4, 1998

link strings

global itsimboli,ckinstr,Istr,IndIstr,tabrif,word

procedure main(args)

itsimboli := &ucase ++ &digits ++ "\"+-*/().,'=" # unione di insiemi

itrapr := "LRJZUVWSMXDPKQ";  # caratteri di icon

itsymb := "().=UVW+-*/^ \""; # simboli

IndIstr := table(0); # tabella indirizzi

tabrif := table(0);  # tabella riferimenti goto e perform

Istr := table("");   # tabella istruzioni

ckinstr := 0; # contaistruzioni

itsrc := args[1];

if not args[1] then itsrc := "source.it";

fl := open(itsrc,"r") | (write("non esiste: ",itsrc) & exit());

#clupsrc := "source.cupl";

itsrc ? clupsrc := tab(find(".",itsrc,1)) || ".cupl"; # elimino dopo simbolo di fine

flo := open(clupsrc,"w")

write("Sorgente IT " || itsrc || " sorgente CUPL " || clupsrc);

write(flo,"COMMENT PROGRAMMA CUPL CHE INTERPRETA PROGRAMMA IT " || itsrc);

while riga := map(read(fl),itrapr,itsymb) do {

  riga ? riga := tab(find(" F",riga,1)); # elimino dopo simbolo di fine

  write(riga)

  ckinstr := ckinstr + 1;

  word := getword(riga); # lista degli operandi

  # inizio scan

  indirizzo := get(word)

  IndIstr[ckinstr] := indirizzo;

  genistr()

#  if \ tabrif[indirizzo] then {

#      if tabrif[indirizzo] == indirizzo then Istr[ckinstr] ||:= "\nLAB" || indirizzo || "\tEND";}

}

every k:=1 to ckinstr do {

  if \ tabrif[IndIstr[k]] then { # esiste un riferimento per goto

     op := tabrif[IndIstr[k]]; if op > 0 then Istr[k] ||:= "\nLAB" || op || "\tEND"

     else if op < 0 then Istr[k] := "LAB" || -op || Istr[k]

  }

  write(Istr[k]);write(flo,detab(Istr[k]));}

end

procedure genistr()

  while *word > 0 do {op1 := get(word)

    if op1 == "IF" then {

       Istr[ckinstr] := "\tIF" || componi(word) || " THEN " || Istr[ckinstr];

       break;

    }

    op1 ? case ch := move(1) of {

      # variabili

      "Y"| "C" | "I": {Istr[ckinstr] ||:= "\tLET " || op1 || componi(word);}

      "H": Istr[ckinstr] ||:= "\tSTOP";

      "T": if not find("WRITE",Istr[ckinstr]) then

            Istr[ckinstr] ||:= "\tWRITE " || get(word)

            else Istr[ckinstr] ||:= ", "  || get(word);

      "G": {op := get(word);Istr[ckinstr] ||:= "\tGO TO LAB" || op;

            tabrif[op] := -op; # per label

           }

      #numerico è un loop

      default: {Istr[ckinstr] ||:= "\tPERFORM LAB" || op1 || " FOR " ||

                               get(word)  || " = " || get(word) ||

                               " BY " || get(word) || " TO "    ||

                               get(word) || "\nLAB" || op1 || "\tBLOCK";

               tabrif[op1] := op1; # per fine perform

      }

  }

}

end

procedure getword(str)

        word := [];

      str ? while tab(upto(itsimboli)) do {

            put(word, tab(many(itsimboli)))

}

  return word

end

procedure componi(lista)

# compone formula, si ferma se finisce o trova IF

  wd := "";

  while op := get(lista) do {

    case op of {

      "IF": {push(lista,op); return wd;}

      "U": {op := "=";}

      "V": {op := "GT";}

      "W": {op := "GE";}

}

  wd := wd || " " || op;

  }

  # risoluzione funzioni

  wd := replace(replace(wd,"\"42E ","SIN("),"\"",")")

  return wd;

end 

 



[1] Tratto da Thomas W. Christopher (1966) Icon Programming Language Handbook