Definizioni syntax-directed Esempio: Notazione infissa Notazione postfissa Produzioni E E 1 + T E E 1 T E T T 0 T 1 T 2... T 9 Regole semantiche E.t := E 1.t _T.t _ + E.t := E 1.t _T.t _ - E.t := T.t T.t := 0 T.t := 1 T.t := 2 T.t := 9
Movimenti di un robot Seq Seq Istr begin Istr est ovest nord sud begin ovest sud est est sud Traduciamo la sequenza in una posizione relativa al punto di partenza: (-1,0) (0,0) (-1,-1) (1,-1) (0,-1) (1,-2)
Produzioni Seq begin Seq Seq 1 Istr Istr est Istr ovest Istr nord Istr sud Regole semantiche Seq.x := 0 Seq.y := 0 Seq.x := Seq 1.x + Istr.dx Seq.y := Seq 1.y + Istr.dy Istr.dx := 1, Istr.dy := 0 Istr.dx := -1, Istr.dy := 0 Istr.dx := 0, Istr.dy := 1 Istr.dx := 0, Istr.dy := -1
Attributi ereditati Dichiarazioni: int NUM, ELEM, K Produzioni D T L L L 1, id L id T int T real Regole semantiche L.in := T.type L 1.in := L.in, addtype (id.entry, L.in) addtype (id.entry, L.in) T.type := integer T.type := real
D T L, int L id 3, L id 2 id 1
D T.type = integer L.in = integer int L.in = integer, id 3, L.in = integer id 2 id 1
Grafo delle dipendenze e ordine di valutazione for ogni nodo n dell albero di parsificazione do for ogni attributo a del simbolo della grammatica associato ad n do costruisci un nodo per a for ogni nodo n dell albero di parsificazione do for ogni regola semantica b := f(c 1,...,c k ) associata alla produzione usata in n do for i = 1 to k do costruisci un arco dal nodo c i al nodo b
Esempio: costruzione di alberi sintattici Funzioni usate: mknode (op, left, right): crea un nodo operatore con label op e i puntatori agli operandi, restituisce il puntatore al nodo creato. mkleaf (id, entry): crea un nodo identificatore con label id e un puntatore alla entry per l identificatore nella symbol-table, restituisce il puntatore al nodo creato. mkleaf (num, val): crea un nodo numero con label num e il valore del numero, restituisce il puntatore al nodo creato.
Definizione syntax-directed per costruire l albero sintattico Produzioni E E 1 + T E E 1 T E T T ( E ) T id T num Regole semantiche E.nptr := mknode ( +, E 1.nptr, T.nptr) E.nptr := mknode ( -, E 1.nptr, T.nptr) E.nptr := T.nptr T.nptr := E.nptr T.nptr := mkleaf (id, id.entry) T.nptr := mkleaf (num, num.val)
E.nptr = + E.nptr = + T.nptr = - E.nptr = - T.nptr = T.nptr = id id id id num num
Esempio: Lista delle differenze Definizione syntax-directed per costruire la lista delle differenze Produzioni C N#L L N;L 1 L N N 0 N 1... N 9 Regole semantiche C.list := L.list L.elem := N.val L.list := N.val - L.elem, L 1.list; L 1.elem := L.elem) L.list := N.val - L.elem N.val := 0 N.val := 1 N.val := 9 list, eval: sintetizzati elem: ereditato
C C.list = 2,-2,4 4#6;2;8 2,-2,4 N.val=4 N # L L.elem = 4 L.list = 2,-2,4 4 N.val=6 N ; L L.elem = 4 L.list = -2,4 6 N N.val=2 ; L L.elem = 4 L.list = 4 2 N N.val=8 8
Definizioni L-attribuiteL A X 1 X 2... X n Ogni attributo ereditato di X j dipende solo da: gli attributi dei simboli X 1 X 2... X j-1 a sinistra di X j nella produzione; gli attributi ereditati di A. Schemi di traduzione Uno schema di traduzione e una grammatica libera in cui gli attributi sono associati ai simboli della grammatica e le azioni semantiche, racchiuse tra parentesi graffe, sono inserite nei membri destri delle produzioni.
Gli schemi di traduzione sono un utile notazione per specificare la traduzione durante la parsificazione. E T R R addop T {print (addop. lessema)} R 1 ε T num {print (num.val)} Bisogna assicurare che il valore di un attributo sia disponibile quando un azione fa ad esso riferimento. Solo attributi sintetizzati: azioni alla fine del membro destro della produzione associata.
Attributi sintetizzati ed ereditati: un attributo ereditato per un simbolo al membro destro di una produzione deve essere calcolato con un azione prima del simbolo un azione non deve far riferimento a un attributo sintetizzato di un simbolo a destra dell azione un attributo sintetizzato per un non terminale a sinistra puo essere calcolato solo dopo che sono stati calcolati tutti gli attributi cui fa riferimento. Le azioni che calcolano questi attributi possono di solito essere posti alla fine del membro destro della produzione.
Definizione non L-attribuita Produzioni A L M A Q R Regole semantiche L.i := l (A.i) M.i := m (L.s) A.s := f (M.s) R.i := r (A.i) Q.i := q (R.s) A.s := f (Q.s)
Schema di traduzione che non soddisfa le condizioni: S A 1 A {A 2 1.in := 1; A 2.in := 2} {print (A.in)} A a Per una definizione diretta dalla sintassi L-attribuita è sempre possibile costruire uno schema di traduzione che soddisfa le tre condizioni precedenti. S {A 1.in := 1} A 1 {A 2.in := 2} A 2 A a {print (A.in)}
Ricorsione sinistra E E 1 num E num {E.val := E 1.val + num.val} {E.val := num.val} Equivalente grammatica senza ricorsione sinistra: E num R R num R ε
E E 1 num E num E num R R num R 1 R ε {E.val := E 1.val + num.val} {E.val := num.val} E E E num num R E num num R num num R ε
E E 1 num E num E num R num R ε {E.val := E 1.val + num.val} {E.val := num.val} {R.i := num.val} R {E.val := R.s} {R1.i := R.i + num.val} R 1 {R.s := R 1.s} {R.s := R.i} E 5+7 E 5+7 3+2 E num 7 3 num R.i=3 E 3 num 2 2 num R.i=3+2 num 3 7 num R.i=5+7 R.s := 1 ε
A A 1 Y A X {A.a := g (A 1.a, Y.y)} {A.a := f (X.x)} A X R R YR ε A X R R Y R 1 R ε {R.i := f (X.x)} {A.a := R.s} {R 1.i := g (R.i, Y.y)} {R.s := R 1.s} {R.s := R.i}
Eliminazione ricorsione sinistra: Esempio E E 1 + T E E 1 -T E T T ( E ) T num {E.val := E 1.val + T.val} {E.val := E 1.val - T.val} {E.val := T.val} {T.val := E.val} {T.val := num.val} La grammatica non è LL(1)
E T R R + T R 1 R - T R 1 R ε T ( E ) T num {R.i := T.val} {E.val := R.s} {R 1.i := R.i + T.val} {R.s := R 1.s} {R 1.i := R.i -T.val} {R.s := R 1.s} {R.s := R.i} {T.val := E.val} {T.val := num.val}
E 4+3=7 E 7 9-5=4 E + T 3 T R 9 9 7 E 9 - T 5 3 num num 9 - T 5 R 9-5=4 7 T 9 num 5 5 num + T 3 4+3=7 R 7 num 9 3 num ε R.i e R.s sono gli attributi, ereditato e sintetizzato rispettivamente, che giocano il ruolo di E.val nella grammatica con ricorsione sinistra.
Eliminazione ricorsione sinistra: Esempio Produzioni E E 1 + T E E 1 T E T T ( E ) T id T num Regole semantiche E.nptr := mknode ( +, E 1.nptr, T.nptr) E.nptr := mknode ( -, E 1.nptr, T.nptr) E.nptr := T.nptr T.nptr := E.nptr T.nptr := mkleaf (id, id.entry) T.nptr := mkleaf (num, num.val)
E T R R + T R 1 R - T R 1 R ε T ( E ) T num T id {R.i := T.nptr} {E.nptr := R.s} {R 1.i := mknode ( +, R.i, T.nptr)} {R.s := R 1.s} {R 1.i := mknode ( -, R.i, T.nptr)} {R.s := R 1.s} {R.s := R.i} {T.nptr := E.nptr} {T.nptr := mkleaf (num, num.val)} {T.nptr := mkleaf (id, id.entry)}
E.nptr = T.nptr = T R.i = R.s = num - T.nptr = R.i = - R.s = num id id + T.nptr = R.i + = R.s = id ε id
E.nptr = + - id num id
Valutazione top-down di grammatiche L-attribuiteL L analizzatore a discesa ricorsiva per grammatiche LL(1) può essere modificato in modo da valutare gli L-attributi. Ad ogni non terminale si associa una funzione che ha come parametri i valori degli attributi ereditati della variabile e restituisce i valori dei suoi attributi sintetizzati. La funzione per un non terminale ha una variabile locale per ogni attributo ereditato o sintetizzato per i simboli che compaiono nelle parti destre delle produzioni dal non terminale.
Codice per le parti destre delle produzioni: Per ogni non terminale B si genera un assegnazione <c 1,..., c k > := B(b 1,... B n ), che è una chiamata alla funzione associata a B. Per ogni terminale a i valori degli attributi sintetizzati vengono assegnati alle corrispondenti variabili e l esame passa al simbolo successivo. Le azioni semantiche vengono ricopiate dopo aver sostituito i riferimenti agli attributi con le variabili corrispondenti.
Valutazione top-down di grammatiche L-attribuiteL function A(e 1,... e n ) var s 1,..., s m, X 1 _x 1,..., X 1 _x k,..., X h _x 1,..., X h _x r begin if cc Gui(A α 1 ) then body (α 1 ) else if cc Gui(A α 2 ) then body (α 2 ). else if cc Gui(A α k ) then body (α k ) else ERRORE( ) return < s 1,..., s m > end
Produzioni C N#L L N;L 1 L N N 0... N 9 ε Regole semantiche C.list := L.list; L.elem := N.val L.list := cons N.val(N.val - L.elem, - L.elem, L 1.list; L 1.list); L 1.elem := L.elem L.list := N.val - L.elem N.val := 0 N.val := 9 C N#L L N;L 1 L ε N 0... N 9 L.list := null Insiemi guida {0, 1,..., 9} {0, 1,..., 9} { } {0} {9}
Schema di traduzione C N# {L.elem := N.val} L {C.list := L.list} L N; {L 1.elem := L.elem} L 1 {cons (N.val - L.elem, L 1.list)} {L.list := null} L ε N 0 {N.val := 0}... N 9 {N.val := 9}
N 0 {N.val := 0}... N 9 {N.val := 9} function N var val begin if cc = 0 then cc := PROSS val := 0 else if cc = 1 then cc := PROSS val := 1. else if cc = 9 then cc := PROSS val := 9 else ERRORE( ) return < s 1,..., s m > end
L N; L ε L 1 {L 1.elem := L.elem} {L.list := cons (N.val - L.elem, L 1.list)} {L.list := null} function L (elem) var list, N_val, L_elem, L_list begin if cc {0, 1,..., 9} then N_val := N if cc = ; then cc := PROSS L_elem := elem L_list := L (L_elem) list := cons (N_val - L_elem, L_list) else ERRORE( ) else if cc = then list := null else ERRORE( ) return list end
C N# L {L.elem := N.val} {C.list := L.list} function C var list, N_val, L_elem, L_list begin if cc {0, 1,..., 9} then N_val := N if cc = # then cc := PROSS L_elem := N_val L_list := L (L_elem) list := L_list else ERRORE( ) else ERRORE( ) return list end