Implementazione


Organizzazione dei package

Dalla fase di progettazione è scaturita la necessità di suddividere l'applicazione in moduli, e visto che il linguaggio di implementazione è Java i moduli corrisponderanno ad altrettanti package.


Interfaccia utente

L'ambiente java mette a disposizione due package per la gestione delle interfacce grafiche: awt e swing. Lo swing prevede funzionalità molto maggiori, ma purtroppo non è ancora supportato dai browser visto che è stato introdotto di recente con il JDK 1.2. Perciò si è preferito adottare l'awt (Abstrac Windows Toolkit), che seppure un po' più povero graficamente permette la creazione semplice di interfacce grafiche gradevoli.

Sono state realizzate due interfacce java InputDevice e OutputDevice, per caratterizzare rispettivamente i dispositivi di ingresso e di uscita. In particolare poi queste interfacce sono state implementate da due nuove classi InputTextArea e OutputTextArea, entrambe derivate dal componente TextArea dell'awt.

L'interprete è stato sviluppato come applicazione stand-alone e non come semplice applet in quanto in questo modo è possibile usufruire della maggiore funzionalità data dai menu. E' stata comunque realizzata un'applet (StarterApplet) in grado di eseguire il programma anche da un browser. L'applicazione è graficamente rappresentata da un frame, in cui è presente una barra dei menu, una barra di stato che fornisce dei suggerimenti all'utente, un pannello di comando con una serie di bottoni ed infine quattro aree di testo relative rispettivamente a input, output, programma e debug. I bottoni mettono a disposizione le principali funzioni dell'interprete, come scelta del linguaggio, valutazione, visualizzazione dell'albero, riscrittura dell'ultimo comando dato in input, ecc. Le restanti funzioni, tra cui anche la gestione dei file, sono accessibili da menu.

Ecco una schermata del programma:

Per quanto riguarda la visualizzazione dell'albero effettuata dal TreeSexpVisitor, si è utilizzato il componente Canvas dell'awt, che permette di definire componenti grafici personalizzati. Ecco un esempio di albero visualizzato dal programma:


Analizzatore lessicale

Per la realizzazione del lexer (classe LispLexer) è stata utilizzata la classe Java predefinita StreamTokenizer e la si è adattata per i propri scopi. Questa scelta è stata limitante sotto certi aspetti in quanto questo componente non è sufficientemente parametrizzabile, ma è stato sicuramente molto più rapido operare in questo modo piuttosto che costruirsi un analizzatore lessicale da zero. Particolari problemi si sono riscontrati nel riconoscimento di numeri con segno. Ad esempio una frase del tipo "3-2" veniva scomposta nei token "3" e "-2" anzichè "3", "-" e "2". Questo è ovviamente dovuto all'ambiguità dei simboli "+" e "-" che possono essere a seconda dei casi operatori oppure simboli di segno. Il problema comunque è stato risolto in fase di parsing, nella quale si poteva attribuire il significato corretto ai token.


Debugging

Nello sviluppo di qualunque software è essenziale la fase di debugging, cioè il controllo passo passo dell'esecuzione del programma. Dato che il JDK non mette a disposizione un tool di debugging usabile facilmente, risulta più comodo stampare dei messaggi di debugging direttamente a video, e per questo è stata prevista infattti un'area di testo apposita. Questa area di testo (che implementa OutputDevice), viene passata al momento della costruzione a lexer, parser e valutatore, in modo che questi vi possano stampare dei messaggi. Il controllo di quali tipi di messaggio stampare viene demandato ad una classe Debug che contiene al suo interno 4 variabili booleane static, che indicano se devono essere stampati i messaggi relativi rispettivamente a lexer, parser, evaluator e environment. Queste variabili possono poi essere impostate dal menu del programma con delle checkbox. Questa scelta è stata fatta perchè se in un futuro volessimo eliminare definitivamente il debug, e volessimo risparmiare il test del valore della variabili in fase di esecuzione, sarebbe sufficiente ridefinire tali variabili come final, cioè in pratica dichiararle delle costanti, e ricompilare il tutto. In questo modo, il compilatore ottimizzerà il codice senza compilare la parte relativa alla scrittura, eseguendo il test in fase di compilazione anzichè di esecuzione.


[ Presentazione | Analisi | Progetto | Implementazione | Codifica | Esecuzione | Conclusioni ]