Costruzione della base comune

    L'interprete è stato inizialmente pensato come interprete Scheme. Tuttavia la realizzazione avrebbe dovuto essere modulare in modo da consentire il riutilizzo di alcune sue componenti per realizzare un secondo interprete, questa volta Prolog. In sostanza lo schema progettuale è il seguente:

figura 2.

    La gerarchia Sexp (Sexp è l'abbreviazione di Symbolic Expression, o S-expression) è una gerarchia di classi Java ideata per rappresentare la forma interna RI scelta per le frasi del linguaggio Scheme. "Il modello computazionale Lisp - quindi Scheme - si fonda sull'idea di denotare i dati e i programmi attraverso espressioni elementari (atomiche) o composte" (cfr con le dispense del corso). In particolare una espressione atomica può denotare un valore o un simbolo, mentre un'espressione composta può essere formata solo da altre espressioni. Il tutto si riassume tramite la seguente gerarchia:

figura 3.

    In essa Sexp è una classe astratta a cui appartengono tutte le espressioni simboliche, ovvero tutte le frasi del linguaggio. AtomSexp e ConsSexp costituiscono rispettivamente le espressioni atomiche e quelle composte. La classe AtomSexp è astratta e da essa derivano due sottoclassi IdentSexp e NilSexp. A NilSexp appartengono le espressioni nulle, indicate con l'atomo predefinito "nil" o con "()". La NilSexp è importante anche perché ad essa appartiene il terminatore delle liste. Le espressioni atomiche non nulle sono delle IdentSexp, nel senso che ciascuna di esse costituisce un identificatore (da cui "ident") che può denotare un simbolo o un valore in funzione del dominio semantico considerato.

    In Scheme ogni espressione è un atomo o un'espressione composta. La valutazione di una S-expression avviene secondo il seguente modello:

In Lisp l'espressione composta costituisce una lista di cui la testa è una funzione e la coda i suoi argomenti, quindi la valutazione dell'espressione (f e1 e2 ... en) consiste nei seguenti passi:


    La gerarchia è quindi utilizzata per costruire una forma interna adeguata a questo schema. In particolare una ConsSexp sarà un oggetto costituito da una testa ed una coda. La sua la coda potrà essere un atomo o una nuova ConsSexp, e così via.
    Ora ricordiamo che in Lisp ogni oggetto non atomico è una lista (Lisp significa List Processing) e la lista è un oggetto che può essere definito in modo ricorsivo come segue:

    La conseguenza di ciò è che la ConsSexp può rappresentare una lista in modo esattamente congruente alla definizione data, ovvero una ConsSexp è una lista se la coda è a sua volta una lista. La ricorsione si chiude definendo la lista vuota: una lista vuota è un oggetto NilSexp. Ora si intuisce l'imporatanza della classe NilSexp, anche se ci si rende conto che di oggetti NilSexp ne serve solo uno, ovvero la lista vuota, la quale è appunto unica in base alla definizione data.

    La gerarchia così costruita è ideata sul modello computazonale Lisp e quindi ci si potrebbe chiedere a questo punto come può adattarsi ad un modello computazionale diverso, quale potrebbe essere quello di un altro linguaggio. In realtà si deve notare che il modello Lisp è sfruttato per gestire la RI della frase del linguaggio e questa non dipendente dal linguaggio scelto. Dunque il compito del riconoscitore è di costruire una RI in forma di Sexp della frase analizzata.

    A riprova di ciò anche l'iterprete Prolog è stato costruito su quella stessa gerarchia.

    In figura 2. sono indicate anche delle utilities. Queste consistono in una serie di classi Java create per fornire alcune utilità. Fra queste alcune classi create appositamente per gestire le Sexp: un classe Lexer generica, una classe per gestire le eccezioni, una classe per costruire un DataBase di Sexp, una classe che definisce un Visitor astratto per Sexp, etc. In particolare la classe Lexer è stata realizzata in modo da consentire di costruire facilmente l'analizzatore lessicale (AL) specifico del linguaggio. L'AL specifico può essere realizzato definendo una sottoclasse della classe Lexer in cui si inizializza l'AL e si ridefiniscono i metodi che devono trattare casi particolare non previsti nella superclasse. L'inizializzazione consiste nel definire quattro insiemi, uno contenete le primitive (classe PrimitiveSet), uno contenente i Tokens speciali (classe SpecialSet), una contenente gli operatori (classe OperatorSet) ed uno contenente i relazionali (classe RelationalSet).
    Delle utilities fanno poi parte anche delle classi sfruttate per la costruzione dell'interfaccia grafica.