PROGETTO DI LINGUAGGI DI PROGRAMMAZIONE A.A. 2000-2001 PARTE II (Prolog) Nella versione in Prolog del progetto diverse cose sono piu` semplici che in ML: 1. non essendoci controlli di tipo, non serve dare un nome diverso ai costruttori dei documenti e dei template (quindi si usano `elem' e `data' sia per gli alberi dei documenti che per i template); 2. inoltre conviene usare atomi (costanti) al posto delle stringhe (per esempio niente doppi apici intorno ai nomi dei tag e degli attributi); 3. in mancanza di controlli di tipo, non conviene piu` chiudere i valori degli attributi dentro un costruttore `data'; le liste di attributi avranno la forma [att1 = val1,...,att_n = val_n] ; il costruttore `data' si usa solo per i pezzi di contenuto testuale degli elementi (PCDATA); ecco quindi un esempio di documento: elem(h1, [background = green], [data 'Home Page']) 4. il Prolog supporta le variabili logiche, quindi niente piu` variabili ?1, ?2, ... ne` $1, $2,... nei template, ma solo variabili Prolog (e niente environment da implementare, ne` funzioni per sostituire le variabili col loro valore nella parte destra delle regole!); neanche `any' serve piu` (basta la variabile anonima prolog `_'); ricordatevi invece di interpretare transl() ! Non essendoci piu` gli environment da implementare ed applicare, per rendere il progetto piu` interessante rendiamo piu` flessibili le modalita` di matching. In particolare, vogliamo permettere anche matching non ordinato e non esaustivo (per esempio, gli elementi elencati nei template non devono necessariamente comparire nello stesso ordine nel documento, e non devono coprire necessariamente tutti gli elementi del documento), e vogliamo gestire anche gli attributi e gli elementi del documento che non vengono menzionati esplicitamente nei template. Per fare un esempio, dobbiamo prima introdurre una nuova notazione. Adesso la lista di attributi di un template nella parte sinistra di una regola di trasformazione puo` avere questa forma: [attr1 = val1, ..., attr_n = val_n] + AltriAtt dove AltriAtt puo` essere una variabile Prolog oppure una lista vuota. In fase di matching, dovete controllare che ogni attr_i compaia tra gli attributi dell'elemento corrente del documento e che il suo valore nel documento unifichi con val_i (come gia` si faceva in ML); in piu`, se questo e` vero, dovete legare AltriAtt ad una lista contenente tutti i restanti attributi del documento (quelli diversi dagli attr_i). Per esempio, se il nodo corrente del documento ha gli attributi [a=1, b=2, c=3] ed il template ha gli attributi [b=2, a=X] + Y dopo il match X deve valere 1 ed Y deve valere [c=3]. Se dopo il + mettiamo una lista vuota, allora non devono "avanzare" attributi. Per esempio se Y fosse [] nell'esempio precedente, allora il match dovrebbe fallire, mentre avrebbe successo legando X a 1 se gli attributi nel documento fossero solo [a=1, b=2]. Il nuovo formato dei contenuti di un elemento nei template e` solo leggermente piu` sofisticato. Possiamo avere: [ .... ] + AltriEl oppure unord [ .... ] + AltriEl. Nel primo caso gli elementi tra quadre devono trovarsi all'inizio del contenuto dell'elemento corrente del documento, e nell'ordine specificato. Per esempio, il matching tra il template [ elem(a,[]+[],[]+[]), elem(b,[]+[],[]+[]) ] + Z ed il pezzo di documento [ elem(a,[],[]), elem(b,[],[]), elem(c,[],[]) ] dovrebbe avere successo, legando Z a [elem(c,[],[])], mentre dovrebbe fallire se il pezzo di documento e` come nei seguenti casi: [ elem(c,[],[]), elem(a,[],[]), elem(b,[],[]) ] [ elem(b,[],[]), elem(a,[],[]), elem(c,[],[]) ] Diversamente, se il template fosse unord [ elem(a,[]+[],[]+[]), elem(b,[]+[],[]+[]) ] + Z ^^^^^ allora il matching avrebbe successo in tutti i casi precedenti, legando Z a [elem(c,[],[])]. Piu` in generale, in questo secondo tipo di template gli elementi elencati tra quadre debbono comparire per forza come figli dell'elemento attuale del documento (cioe` non innestati dentro ulteriori elementi), ma in qualunque posizione. Ecco infine un esempio di una regola completa. elem( car, [producer=X]+AltriAtt, []+VecchiEl ) ==> elem( car, AltriAtt, [ elem(producer,[],[data X]) | VecchiEl] ) Questa regola trasforma gli elementi `car' trasformando l'attributo producer in un elemento, e preservando gli altri attributi (attraverso la variabile AltriAtt) e i vecchi elementi di car (attraverso la variabile VecchiEl). La seguente regola invece effettua la trasformazione inversa (buttando via gli eventuali attributi di producer: elem( car, VecchiAt, unord[ elem(producer,[]+_,[data X]) ]+AltriEl ) ==> elem( car, [producer=X | VecchiAt], AltriEl ) Notate l'importanza di `unord' (cosi` l'elemento producer di car non deve essere necessariamente il primo elemento del contenuto; tuttavia deve essere un figlio diretto di car). Per implementare il nuovo matching, usare tecniche generate-and-test che tentano tutte le possibili corrispondenze tra gli elementi nel template e quelli nel documento. Possono essere utili predicati come il `select' dato per esercizio durante il corso. Per ulteriori dettagli e chiarificazioni contattare il docente (si consiglia comunque di contattarlo ad ogni scelta implementativa per non rischiare di imboccare strade sbagliate inutilmente complicate...) Verranno anche distribuiti raffinamenti opzionali del progetto.