L'argomento trattato in questa sezione è ripreso
e discusso più in generale alla pagina "Estensione
dell'interprete". Qui si tratta l'argomento con riferimento specifico
al caso Prolog.
Quanto detto nella pagina precedente a proposito
dell'estensione dell'interprete Scheme
vale anche per l'estensione in Prolog. Anche per la parte Prolog si è
infatti cercato di realizzare codice espandibile e modulare.
Qui di seguito si descrivono i passi necessari nel
caso in cui si voglia aggiungere una funzionalità all'interprete
Prolog. Come era stato fatto nel caso Scheme si distinguono anche questa
volta le due possibilità, ovvero quella di modificare il codice
esistente e quella di aggiungere nuovo codice. Anche il procedimento è
sostanzialmente sempre lo stesso e consiste nel far riconoscere all'interprete
il nuovo operatore ed indicargli come valutarlo. Tuttavia nel caso Prolog
vi sono alcuni passi in più rispetto al caso Lisp. Complessivamente
i punti da trattare sono i seguenti:
Sintesi delle caratteristiche della nuova funzionalità da introdurre.
In Prolog per aggiungere una nuova funzionalità si può aggiungere
un termine predefinito oppure un operatore: se si vuole aggiungere un termine
predefinito si dovrà stabilire il funtore, il numero ed il tipo
di argomenti; se si vuole aggiungere un operatore si devono stabilire la
priorità ed il
tipo (ovvero
un valore nell'insieme {fx, fy, xfx, xfy, yfx, xf, yf}).
Costruzione della classe dedicata all'operatore. Si deve definire
la nuova classe associata all'operatore/termine. Se stiamo definendo un
termine la nuova classe dovrà essere una sottoclasse di TermSexp.
Se invece stiamo definendo un nuovo operatore allora la nuova classe dovrà
essere una sottoclasse di PrefixSexp, InfixSexp o PostfixSexp a seconda
che si tratti di un operatore prefisso, infisso o postfisso (in realtà
deve essere sottoclasse della classe più specifica possibile, per
esempio se si tratta di un operatore infisso aritmetico allora la nuova
classe deve derivare da InfixExprSexp che è la sottoclasse di InfixSexp
dedicata agli operatori aritmetici). I soli metodi da implementare
sono il costruttore (che chiama super e setta la priorità) e l'accept().
Analisi lessicale (lexer). Se il nome scelto è costituito
da una stringa formata da caratteri alfabetici (ovvero non si usano caratteri
particolare) allora l'AL è già correttamente funzionante.
Analisi sintattica (parser). Alla costruzione della rappresentazione
interna (RI) della frase partecipano oltre al parser (un oggetto della
classe NewParser) anche le classi OpSexp e SetOps. Queste due classi servono
a costruire le tabelle contenenti i vari operatori e a gestire la costruzione
degli oggetti corrispondenti alle classi associate a ciascun operatore.
Qui basti sapere che per riconoscere un operatore o un termine predefinito
vengono sfruttate queste due classi. Vedremo fra poco in che modo si procede
a seconda che si voglia modificare il codice esistente oppure estenderlo.
Valutazione (visitor). Si deve infine aggiungere il codice per la
valutazione del nuovo operatore/termine. anche in questo caso le vie possibili
sono due: modifica o estensione.
A questo punto non resta che vedere come devono essere
sviluppati gli ultimi due punti dell'elenco, diversificando i due casi
citati, ovvero modifica ed estensione:
Modifica del codice (modularità).
Poiché si ha a disposizione il codice il procedimento è evidentemente
molto veloce. Infatti riguardo all'analisi sintattica si deve semplicemente
aggiungere una riga di codice al file SetOps.Java.
La riga da aggiungere sarà del tipo:
p.SpecificSet.put(name, new OpSexp(name,
priorityOrArity,
type,
Class.forName(completeName)));
ove:
SpecificSet = ...
... = prefixOpSet (per gli operatori prefissi)
... = infixOpSet (per gli operatori infissi)
... = postfixOpSet (per gli operatori postfissi)
... = TermSet (per i termini)
name = stringa indicante il nome dell'operatore/termine
priorityOrArity = valore intero indicate la priorità dell'operatore,
ovvero l'arità del termine
type = tipo dell'OpSexp che viene creato
completeName = stringa contenente il nome della classe dedicata
al nuovo operatore (nome completo).
Tale riga di codice dovrà essere inserita dopo la riga:
p.SpecificSet = new SexpHashtable();
ove lo SpecificSet è quello utilizzato dalla riga aggiunta.
Per ciò che riguarda la valutazione per prima cosa si modificherà
il file PrologSexpVisitor.java aggiungendo la dichiarazione del metodo
visit(nuovaClasse):
/**
* Effettua la visita di un nuovaClasse.
* @param e oggetto che viene visitato.
*/
public abstract void visit(nuovaClasse e) throws InterpreterException;
Fatto ciò si deve aggiungere la definizione dello stesso metodo
nella classe EngineSexpVisitor. Se si tratta di un operatore aritmetico
(che quindi è non callable) quel metodo deve semplicemente generare
un'eccezione indicante che si sta cercando di applicare la risoluzione
ad un termine aritmetico. Il codice per l'esecuzione della nuova operazione
andrà messo nella classe EvalVisitor.
Aggiunta di nuovo codice (estensibilità
incrementale). Seguendo questa seconda via ciò che si
fa è di creare sottoclassi delle classi che nel caso precedente
dovevano essere modificate, ovvero SetOps, PrologSexpVisitor, EngineSexpVisitor
ed eventualmente anche EvalVisitor. In realtà non è necessario
ridefinire la classe SetOps poiché si può sfruttare il metodo
setPrimitive(OpSexp)
della classe NewParser. Quindi è sufficiente definire le sottoclassi
dei soli visitor e richiamare quel metodo dal costruttore della sottoclasse
dell'EngineSexpVisitor.
Il tutto può risultare più chiaro
analizzando un esempio. A tal proposito è stato aggiunto all'interprete
il package Extension ed in esso sono stati messi i file relativi all'aggiunta
(incrementale) dell'operatore sqr (elevamento al quadrato). Tale operatore
è stato aggiunto con analoga metodologia anche nell'interprete Scheme
ed all'interfaccia. Quindi al package Extension appartengono le classi
relative all'incremento dell'interprete Scheme (ExtendedLispEvalSexpVisitor),
dell'interprete Prolog (SqrSexp, ExtendedPrologEngineVisitor
ed ExtendedPrologEvalVisitor) e dell'interfaccia (Main).
I codici relativi sono mostrati nella pagina "Estensionedell'interprete".