SlideShare a Scribd company logo
LFC




                                     Parsing di
                                     linguaggi liberi
                                     Introduzione
                                     Parsing top-down
                                     Parsing bottom-up



Linguaggi formali e compilazione
    Corso di Laurea in Informatica




           A.A. 2008/2009
LFC
Linguaggi formali e compilazione
                                   Parsing di
                                   linguaggi liberi
                                   Introduzione
                                   Parsing top-down
                                   Parsing bottom-up




Parsing di linguaggi liberi
   Introduzione
   Parsing top-down
   Parsing bottom-up
LFC
Linguaggi formali e compilazione
                                   Parsing di
                                   linguaggi liberi
                                   Introduzione
                                   Parsing top-down
                                   Parsing bottom-up




Parsing di linguaggi liberi
   Introduzione
   Parsing top-down
   Parsing bottom-up
LFC
Parsing di linguaggi liberi
      Ogni linguaggio libero può essere riconosciuto in
  ◮                                                             Parsing di
                                                                linguaggi liberi
      tempo proporzionale al cubo della lunghezza della         Introduzione

      stringa in input.                                         Parsing top-down
                                                                Parsing bottom-up


      L’algoritmo che stabilisce questo limite di
  ◮
      complessità, basato su una classica applicazione
      della programmazione dinamica, è tuttavia
      inutilizzabile in applicazioni reali.
      Gli algoritmi di parsing utilizzati nella pratica sono
  ◮
      generalmente classificati base alla strategia utilizzata
      per la costruzione (anche solo “ideale”) del
      parse-tree, top-down o bottom-up.
      All’interno di queste due categorie generali, gli
  ◮
      algoritmi si differenziano in base a scelte strategiche
      più dettagliate.
      Tali algoritmi non possono analizzare tutti i linguaggi
  ◮
      context-free.
LFC
Tipi di parser
                                                             Parsing di
                                                             linguaggi liberi
                                                             Introduzione
                                                             Parsing top-down
      In concreto, i tipi di parser più diffusi includono:
  ◮                                                          Parsing bottom-up


            parser a discesa ricorsiva (top-down),
        ◮

            parser di tipo LR (bottom-up),
        ◮

            parser a precedenza di operatori (bottom-up).
        ◮

      Nel parsing top-down si cerca una derivazione
  ◮
      canonica per la stringa/programma in input partendo
      dall’assioma iniziale.
      La costruzione top-down dell’albero di derivazione
  ◮
      corrisponde in modo naturale al riconoscimento di un
      blocco/procedura/espressione in termini delle parti
      costituenti.
LFC
Tipi di parser (continua)
                                                               Parsing di
                                                               linguaggi liberi
                                                               Introduzione
                                                               Parsing top-down

      Nel parsing bottom-up si cerca una derivazione           Parsing bottom-up
  ◮
      canonica per la stringa/programma in input partendo
      dalla stringa stessa e applicando riduzioni
      successive.
      Una riduzione non è nient’altro che l’applicazione “in
  ◮
      senso contrario” di una produzione della grammatica.
      La costruzione bottom-up dell’albero di derivazione
  ◮
      corrisponde in modo naturale al riconoscimento di
      singole porzioni di un programma e alla loro
      composizione in parti più complesse.
LFC
Non determinismo
                                                                Parsing di
                                                                linguaggi liberi
                                                                Introduzione
      In entrambi i tipi di parser (top-down o bottom-up) è
  ◮                                                             Parsing top-down
                                                                Parsing bottom-up
      possibile (anche se non desiderabile) che la scelta
      della produzione da utilizzare, in una delle due
      direzioni, non sia determinabile con certezza.
      Se la scelta di una produzione è “errata” il
  ◮
      riconoscimento fallisce.
      In tal caso, prima di dichiarare che la stringa è
  ◮
      sintatticamente errata, è necessario operare un
      backtracking sull’input, ritornando al punto di scelta.
      Per i parser a discesa ricorsiva (e implementati
  ◮
      mediante ricorsione) ciò può essere sufficientemente
      agevole, anche se computazionalmente pesante.
LFC
Linguaggi formali e compilazione
                                   Parsing di
                                   linguaggi liberi
                                   Introduzione
                                   Parsing top-down
                                   Parsing bottom-up




Parsing di linguaggi liberi
   Introduzione
   Parsing top-down
   Parsing bottom-up
LFC
Parsing a discesa ricorsiva (recursive
descent)
                                                                    Parsing di
                                                                    linguaggi liberi
                                                                    Introduzione
                                                                    Parsing top-down
                                                                    Parsing bottom-up

      Un parser a discesa ricorsiva (d.r.) può essere
  ◮
      realizzato come collezione di procedure
      (mutuamente) ricorsive, una per ogni simbolo non
      terminale della grammatica.
      Siano A → α1 . . . αk le produzioni relative al
  ◮
                                    i      i
      nonterminale A, e sia αi = X1 . . . Xni , i = 1, . . . , k.
      La procedura relativa al simbolo A è illustrata nel
  ◮
      seguente algoritmo, in cui (per semplicità di
      notazione) evitiamo i doppi indici e supponiamo che
      il simbolo corrente di input sia contenuto nella
      variabile globale x.
LFC




                                                   Parsing di
                                                   linguaggi liberi
                                                   Introduzione
                                                   Parsing top-down
Algorithm 1 Procedura per il nonterminale A        Parsing bottom-up



                                               →
 1: Scegli “opportunamente” una produzione A
    X1 X2 . . . Xk (Xj ∈ V)
 2: for j = 1, . . . , k do
       if Xj ∈ N then
 3:
           Xj ()
 4:
       else if Xj = x then
 5:
           x ← read()
 6:
       else
 7:
           error()
 8:
LFC
Backtracking
      La scelta della produzione da applicare (riga 1            Parsing di
  ◮
                                                                 linguaggi liberi
      dell’algoritmo 1) può essere meno critica se, nel caso     Introduzione
                                                                 Parsing top-down

      di fallimento, viene effettuato backtracking sull’input.   Parsing bottom-up



      L’algoritmo deve essere modificato in modo tale da
  ◮
      dichiarare fallimento solo quando tutte le produzioni
      per il nonterminale A sono state provate senza
      successo.
      Deve però essere disposto un meccanismo per
  ◮
      riposizionare il puntatore di input su quello che era il
      simbolo corrente al momento della chiamata della
      procedura.
      Ne consegue che: o il parser bufferizza l’input
  ◮
      ricevuto dallo scanner, oppure è quest’ultimo che
      deve essere opportunamente programmato per la
      ripetizione di certe sequenze di token.
LFC
Scelta della produzione
      Fortunatamente, in molti casi concreti il backtracking
  ◮                                                             Parsing di
                                                                linguaggi liberi
      non è necessario perché la scelta della produzione        Introduzione
                                                                Parsing top-down
      può essere fatta con lookahead limitato (cioè             Parsing bottom-up


      guardando pochi token a partire dalla posizione
      corrente di input).
      Consideriamo la semplice grammatica:
  ◮


                           A → aA | bB
                           B → ǫ | bB

      Per entrambi i non terminali, la scelta della
  ◮
      produzione da usare può essere fatta guardando il
      prossimo simbolo x in input.
            Per A: se x = a, usa la produzione A → aA; se
        ◮

            x = b, usa la produzione A → bB.
            Per B. se x = $ (end of input), usa la produzione
        ◮

            B → ǫ; se x = b, usa la produzione B → bB;
LFC
Grammatiche inadatte al parsing a discesa
ricorsiva
                                                                 Parsing di
      Alcune produzioni sono inadatte al r.d. parsing.           linguaggi liberi
  ◮
                                                                 Introduzione

      Produzioni con ricorsioni a sinistra, che possono
  ◮                                                              Parsing top-down
                                                                 Parsing bottom-up

      causare cicli infiniti, come nella grammatica
                             → E +T | T
                        E
                             → T ∗F | F
                        T
                             → id | (E )
                         F
      Produzioni con prefissi comuni (come il noto caso del
  ◮
      “dangling else”), che rendono non limitabile a priori la
      quantità di lookahead. Un altro esempio (artificiale) è
                         A → aB | aC
                         B → aB | b
                         C → aC | c
      Come sappiamo, esistono opportune trasformazioni
  ◮
      per risolvere questi problemi.
LFC
FIRST e FOLLOW
     Data una grammatica G, l’implementazione di parser
 ◮
                                                                 Parsing di
     a d.r. per G utilizza (secondo le “teorie” più accettate)   linguaggi liberi
                                                                 Introduzione
     due funzioni, dette FIRST e FOLLOW , che                    Parsing top-down
                                                                 Parsing bottom-up
     determinano la scelta della produzione da utilizzare.
     Data G = (N , T , P, S) e data una stringa
 ◮
     α ∈ (N ∪ T )∗ , si definisce FIRST (α) l’insieme dei
     simboli terminali con cui può iniziare una frase
     derivabile da α, più eventualmente ǫ se α ⇒∗ ǫ:
           FIRST (α) = {x ∈ T |α ⇒∗ xβ, β ∈ T ∗ }
                            ∪ {ǫ} , se α ⇒∗ ǫ.
     Per ogni nonterminale A ∈ N si definisce
 ◮
     FOLLOW (A) l’insieme dei terminali che si possono
     trovare immediatamente alla destra di A in una
     qualche forma di frase. In altri termini,
     x ∈ FOLLOW (A) se S ⇒∗ αAxβ, con α e β
     opportuni.
LFC
Calcolo di FIRST (α)
                                                                        Parsing di
                                                                        linguaggi liberi
                                                                        Introduzione
      Definiamo innanzitutto come si calcola FIRST (α) nel
  ◮                                                                     Parsing top-down
                                                                        Parsing bottom-up
      caso in cui α sia un singolo simbolo della
      grammatica, cioè α = X con X ∈ N ∪ T .
            Se X è un terminale, si pone naturalmente
        ◮

            FIRST (X ) = {X };
            se X è un nonterminale poniamo innanzitutto
        ◮

            FIRST (X ) = {}.
                  Se esiste la produzione X → X1 . . . Xn , e risulta
              ◮

                  ǫ ∈ FIRST (Xj ), j = 1, . . . , k − 1, poniamo
                  FIRST (X ) = FIRST (X ) ∪ {x} per ogni simbolo
                  x ∈ FIRST (Xk ).
                  Infine, se esiste la produzione X → ǫ oppure
              ◮

                  ǫ ∈ FIRST (Xj ), j = 1, . . . , k , poniamo
                  FIRST (X ) = FIRST (X ) ∪ {ǫ}.
LFC
Esempi
     Si consideri nuovamente la grammatica
 ◮
                                                   Parsing di
                                                   linguaggi liberi
                                      ′    ′
                         → (E ) E | id E
                    E                              Introduzione
                                                   Parsing top-down
                                                   Parsing bottom-up
                    E′ → + E E′ | × E E′ | ǫ

     Per questa grammatica risulta
 ◮
           FIRST (E) = {( , id}
       ◮

           FIRST (E ′ ) = {+, ×, ǫ}
       ◮

     Si consideri ora la grammatica per an bm ck
 ◮


                         A → aA | BC | ǫ
                         B → bB | ǫ
                         C → cC | ǫ

     Per questa grammatica risulta
 ◮
           FIRST (C) = {c, ǫ}
       ◮

           FIRST (B) = {b, ǫ}
       ◮

           FIRST (A) = {a, b, c, ǫ}
       ◮
LFC
Esempio
                                                           Parsing di
                                                           linguaggi liberi
                                                           Introduzione
                                                           Parsing top-down

     Si riconsideri la grammatica per le espressioni che
 ◮                                                         Parsing bottom-up


     “forza” la precedenza di operatori:

                             → E +T | T
                        E
                             → T ×F | F
                         T
                             → id | (E )
                         F

     Per questa grammatica risulta
 ◮
           FIRST (F ) = {( , id};
       ◮

           FIRST (T ) = FIRST (F );
       ◮

           FIRST (E) = FIRST (T ).
       ◮
LFC
Calcolo di FIRST (α) (continua)
                                                                 Parsing di
                                                                 linguaggi liberi
                                                                 Introduzione

      Il calcolo di FIRST (α), dove α = X1 . . . Xn è una        Parsing top-down
  ◮
                                                                 Parsing bottom-up

      generica stringa di terminali e nonterminali, può ora
      essere svolto nel modo seguente
      a ∈ FIRST (α) se e solo se, per qualche indice
  ◮
      k ∈ 1, . . . n − 1, risulta a ∈ Xk e ǫ ∈ Xj ,
      j = 1, . . . , k − 1 (si suppone sempre ǫ ∈ FIRST (X0)).
      Se inoltre ǫ ∈ FIRST (Xj ), j = 1, . . . , n, allora
  ◮
      ǫ ∈ FIRST (α).
      Ad esempio, nel caso della seconda grammatica del
  ◮
      lucido precedente risulta: FIRST (aA) = {a} e
      FIRST (BC) = {b, c, ǫ}.
LFC
Calcolo di FOLLOW (A)
                                                              Parsing di
                                                              linguaggi liberi
                                                              Introduzione

      Il calcolo di FOLLOW (A), per un generico non           Parsing top-down
  ◮
                                                              Parsing bottom-up

      terminale A, può essere svolto in questo modo.
      Se A = S, si inserisce il simbolo speciale $
  ◮
      (marcatore di fine input) in FOLLOW (A).
      Se esiste la produzione B → αAβ, tutti i terminali in
  ◮
      FIRST (β) si inseriscono in FOLLOW (A); inoltre, se
      ǫ ∈ FIRST (β), tutti i terminali che stanno in
      FOLLOW (B) si inseriscono in FOLLOW (A) .
      Analogamente, se esiste la produzione B → αA, tutti
  ◮
      i terminali che stanno in FOLLOW (B) si inseriscono
      in FOLLOW (A) .
LFC
Esempio
                                                               Parsing di
     Consideriamo la solita grammatica
 ◮
                                                               linguaggi liberi
                                                               Introduzione
                                                               Parsing top-down
                                   ′        ′
                          → (E ) E | id E
                  E                                            Parsing bottom-up


                      ′            ′            ′
                          → +E E | × E E | ǫ
                  E

     Possiamo subito stabilire che FOLLOW (E ) include il
 ◮
     simbolo $ e il simbolo ); inoltre contiene i simboli in
     FIRST (E ′) (eccetto ǫ) e cioè + e ×.
     FOLLOW (E ′) include FOLLOW (E ), a causa (ad
 ◮
     esempio) della produzione E → idE ′ .
     La produzione E ′ → +EE ′ , unitamente a E ′ → ǫ,
 ◮
     stabilisce che vale anche il contrario, e cioè che
     FOLLOW (E ) include FOLLOW (E ′ ).
     Mettendo tutto insieme si ottiene
 ◮
     FOLLOW (E ) = FOLLOW (E ′) = {$, ) , +, ×}.
LFC
Esempio
                                               Parsing di
                                               linguaggi liberi
                                               Introduzione
                                               Parsing top-down
                                               Parsing bottom-up



     Per la grammatica
 ◮


                    A → aA | BC | ǫ
                    B → bB | ǫ
                    C → cC | ǫ

     risulta FOLLOW (A) = FOLLOW (C) = {$} e
     FOLLOW (B) = {c, $}.
LFC
Esempio
                                                      Parsing di
                                                      linguaggi liberi
                                                      Introduzione
                                                      Parsing top-down
                                                      Parsing bottom-up
     Per la grammatica con precedenza di operatori:
 ◮


                           → E +T | T
                      E
                           → T ×F | F
                      T
                           → id | (E )
                       F

     risulta:
           FOLLOW (E) = {$, +, )};
       ◮

           FOLLOW (T ) = FOLLOW (E) ∪ {×};
       ◮

           FOLLOW (F ) = FOLLOW (T ).
       ◮
LFC
Grammatiche LL(1)
      Per opportune classi di grammatiche libere è
  ◮
                                                                  Parsing di
      possibile realizzare parser a d.r. che non richiedono       linguaggi liberi
                                                                  Introduzione

      backtracking (tali parser sono anche detti predittivi).     Parsing top-down
                                                                  Parsing bottom-up


      Una tale classe di grammatiche è la LL(1), dove le
  ◮
      due L stanno per “left-to-right” e “leftmost derivation”.
      In altri termini, il parsing avviene leggendo l’input
  ◮
      unidirezionalmente, da sinistra a destra, e
      costruendo una derivazione canonica sinistra della
      stringa. Il parametro 1 indica che è richiesto un solo
      simbolo di lookahead.
      La classe LL(1) è in grado di esprimere molte fra le
  ◮
      caratteristiche della sintassi dei linguaggi di
      programmazione.
      Nessuna grammatica ricorsiva a sinistra o con
  ◮
      produzioni aventi previssi comuni può però essere
      LL(1) (come pure nessuna grammatica ambigua).
LFC
Grammatiche LL(1) (continua)
                                                              Parsing di
                                                              linguaggi liberi
      Come possiamo stabilire se una grammatica è
  ◮                                                           Introduzione
                                                              Parsing top-down

      LL(1)?                                                  Parsing bottom-up



      Consideriamo la procedura per un generico
  ◮
      nonterminale A.
      Se esistono due produzioni A → α e A → β, allora
  ◮
      certamente non deve accadere che

                  FIRST (α) ∩ FIRST (β) = {} ,

      perché in tal caso un solo carattere di lookahead non
      basterebbe per decidere correttamente la
      produzione da usare.
      Tuttavia ciò potrebbe non essere sufficiente.
  ◮
LFC
Grammatiche LL(1) (continua)
                                                              Parsing di
                                                              linguaggi liberi
                                         ⇒∗
      Supponiamo (ad esempio) che α           ǫ. Potremmo
  ◮                                                           Introduzione
                                                              Parsing top-down

      trovarci nella seguente situazione:                     Parsing bottom-up




               S ⇒∗ γAxδ,     γ ∈ T ∗, x ∈ T , δ ∈ V ∗

      dove, se il prossimo carattere di input fosse proprio
      x, potrebbe essere corretto usare la produzione
      A → α.
      Tuttavia, se x fosse anche in FIRST (β) potrebbe
  ◮
      essere corretto pure usare la produzione A → β.
      Deve quindi risultare α ⇒∗ ǫ implica
  ◮
      FOLLOW (A) ∩ FIRST (β) = {} e, analogamente,
      β ⇒∗ ǫ implica FOLLOW (A) ∩ FIRST (α) = {}
LFC
Esempio
     Si consideri la grammatica
 ◮
                                                                Parsing di
                                                                linguaggi liberi
                          A → BE                                Introduzione
                                                                Parsing top-down

                          B → C|D                               Parsing bottom-up




                         C → ǫ | cc
                         D → ǫ | dd
                             → c|d
                         E

     Poiché risulta FIRST (C) ∩ FIRST (D) = {ǫ}, la
 ◮
     grammatica non è LL(1).
     Infatti, supponiamo che la stringa in input inizi con c.
 ◮
     Dopo la riscrittura dell’assioma il parser verrebbe a
     trovarsi con la forma di frase BE e il carattere c in
     input.
     A questo punto potrebbe essere corretto derivare
 ◮
     tanto CE (se l’input fosse, ad esempio, ccd) quanto
     DE (se l’input fosse c).
LFC
Esempio
     Si modifichi la precedente grammatica nel modo
 ◮
                                                               Parsing di
     seguente                                                  linguaggi liberi
                                                               Introduzione
                                                               Parsing top-down
                                                               Parsing bottom-up
                         A → BE
                         B → C|D
                         C → ǫ | cc
                         D → dd
                             → c|d
                         E

     (cancellando cioè la produzione D → ǫ).
     Poiché risulta FIRST (D) ∩ FOLLOW (B) = {d}, la
 ◮
     grammatica non è LL(1).
     Il problema si verifica con input che inizia con d,
 ◮
     perché potrebbe essere corretto (dopo la derivazione
     iniziale) derivare tanto CE (se l’input fosse d) quanto
     DE (se l’input fosse, ad esempio ddc).
LFC
Costruzione della tabella di parsing
                                                               Parsing di
      Per una grammatica LL(1) è possibile costruire           linguaggi liberi
  ◮
                                                               Introduzione
      (utilizzando le funzioni FIRST e FOLLOW ) una            Parsing top-down
                                                               Parsing bottom-up
      tabella, detta tabella di parsing, che, per ogni
      nonterminale A e ogni terminale x, prescrive il
      comportamento del parser.
      Indicheremo con MG la tabella di parsing relativa alla
  ◮
      grammatica G (o semplicemente con M, se la
      grammatica è evidente).
      La generica entry MG [A, x] della tabella può
  ◮
      contenere una produzione A → α di G, oppure
      essere vuota, ad indicare che si è verificato un
      errore.
      Disponendo di MG , la prima riga dell’algoritmo 1
  ◮
      viene sostituita da un lookup a MG [A, x].
LFC
Costruzione della tabella di parsing
(continua)
                                                                 Parsing di
                                                                 linguaggi liberi
                                                                 Introduzione
                                                                 Parsing top-down
      L’algoritmo di costruzione della parsing table è molto
  ◮                                                              Parsing bottom-up


      semplice ed è formato da un ciclo principale nel
      quale si prendono in considerazione tutte le
      produzioni.
      Per ogni produzione A → α:
  ◮
            per ogni simbolo x in FIRST (α) si pone
        ◮

            M[A, x ] = ‘A → α′ ;
            se ǫ ∈ FIRST (α), per ogni simbolo y in FOLLOW (A)
        ◮

            si pone M[A, y ] = ‘A → α′ .
      Tutte le altre entry della tabella vengono lasciate
  ◮
      vuote (ad indicare l’occorrenza di un errore).
      Se la grammatica è LL(1), nessuna entry della
  ◮
      tabella viene riempita con più di una produzione.
LFC
Esempio
     Consideriamo ancora la grammatica con precedenza
 ◮
                                                             Parsing di
     di operatori:                                           linguaggi liberi
                                                             Introduzione

                           → E +T | T
                      E                                      Parsing top-down
                                                             Parsing bottom-up


                           → T ×F | F
                      T
                           → id | (E )
                      F
     Tale grammatica (pur non ambigua) non è LL(1)
 ◮
     perchè (p.e.), entrambe le produzioni E → E + T e
     E → T verrebbero poste nella entry M[E , id].
     Eliminando la left-recursion si può però ottenere una
 ◮
     grammatica equivalente che è LL(1):
                           → TE ′
                       E
                       E ′ → +TE ′ | ǫ
                           → FT ′
                       T
                       T ′ → ×FT ′ | ǫ
                           → id | (E )
                       F
LFC
Esempio (continua)
      Per la grammatica modificata
  ◮
                                                                  Parsing di
                                                                  linguaggi liberi
                                      ′
                              → TE
                          E                                       Introduzione
                                                                  Parsing top-down
                                                                  Parsing bottom-up
                          E ′ → +TE ′ | ǫ
                              → FT ′
                          T
                          T ′ → ×FT ′ | ǫ
                              → id | (E )
                          F

      abbiamo:
            FIRST (F ) = FIRST (T ) = FIRST (E) = {id, ( };
        ◮

            FIRST (E ′ ) = {+, ǫ};
        ◮

            FIRST (T ′ ) = {×, ǫ};
        ◮

            FOLLOW (E) = FOLLOW (E ′ ) = {$, )};
        ◮

            FOLLOW (T ) = (FIRST (E ′ ) {ǫ}) ∪ FOLLOW (E ′ ) =
        ◮

            {+, $, )};
            FOLLOW (T ′ ) = FOLLOW (T ) = {+, $, )};
        ◮

            FOLLOW (F ) = (FIRST (T ′ ) {ǫ}) ∪ FOLLOW (T ′ ) =
        ◮

            {×, +, $, )}.
LFC
Esempio (continua)
                                                                                           Parsing di
                                                                                           linguaggi liberi
                                                                                           Introduzione
                                                                                           Parsing top-down
                                                                                           Parsing bottom-up

      L’algoritmo produce la seguente tabella di parsing
  ◮


                                             Simbolo di input
       N.T.
                            (            )                +             ×
                 id                                                                    $
              E → TE ′   E → TE ′
       E
       E′                            ′               ′          ′                  ′
                                    E →ǫ            E → +TE                       E →ǫ
              T → FT ′   T → FT ′
       T
       T′                           T′ → ǫ            T′ → ǫ        T ′ → ×FT ′   T′ → ǫ
              F → id     F → (E)
       F



      in cui le entry vuote corrispondono ad una situazione
      di errore.
LFC
Esempio
     Se tentassimo di produrre la tabella di parsing per la
 ◮                                                                                    Parsing di
                                                                                      linguaggi liberi
     grammatica                                                                       Introduzione
                                                                                      Parsing top-down
                                                                                      Parsing bottom-up
                                           ′          ′
                             → (E ) E | id E
                         E
                         E′ → + E E′ | × E E′ | ǫ

     otterremmo

                                         Simbolo di input
      N.T.
                             (           )             +           ×
                id                                                             $
             E → idE ′    E → (E)E ′
       E
                                                 E ′ → +EE ′   E ′ → ×EE ′
      E′                               E′ → ǫ                                E′ → ǫ
                                                    E′ → ǫ        E′ → ǫ




     Con in input il carattere + o il carattere ×, il parser
 ◮
     non saprebbe quindi come procedere.
     Il non soddisfacimento delle proprietà LL(1) è una
 ◮
     conseguenza dell’ambiguità della grammatica.
LFC
Esempio (continua)
                                                               Parsing di
                                                               linguaggi liberi
                                                               Introduzione
      In questo caso è comunque possibile forzare nel
  ◮                                                            Parsing top-down
                                                               Parsing bottom-up
      parser una regola di scelta senza compromettere la
      capacità di riconoscere il linguaggio generato dalla
      grammatica.
      Possiamo (anzi, dobbiamo!) semplicemente optare
  ◮
      in favore delle produzioni E ′ → +EE ′ e E ′ → ×EE ′ ,
      anziché E ′ → ǫ.
      Si noti comunque che, pur avendo risolto l’ambiguità,
  ◮
      l’interpretazione delle espressioni che deriva
      dall’albero di parsing non è quella “naturale” (non
      viene soddisfatta la precedenza naturale degli
      operatori).
LFC
Esempio
     Consideriamo la seguente grammatica:
 ◮
                                                          Parsing di
                                                          linguaggi liberi
                  S → iE tS eS | iE tS | a                Introduzione
                                                          Parsing top-down

                      →b
                  E                                       Parsing bottom-up




     che mette a fuoco il problema del dangling else di
     molti linguaggi e che, avendo produzioni con un
     prefisso comune, non è idonea al parsing a d.r.
     Operando nel modo già illustrato, otteniamo:
 ◮

                       S → i E t S S′ | a
                      S′ → e S | ǫ
                           →b
                       E
     e risulta
           FIRST (S) = {i, a};
       ◮

           FIRST (S ′ ) = {e, ǫ};
       ◮

           FIRST (E) = {b};
       ◮

           FOLLOW (S) = FOLLOW (S ′ ) = {$, e}.
       ◮
LFC
Esempio (continua)
                                                                         Parsing di
      La grammatica modificata è non è ancora LL(1) in                    linguaggi liberi
  ◮
                                                                         Introduzione

      quanto FIRST (eS) ∩ FOLLOW (S ′ ) = {e}.                           Parsing top-down
                                                                         Parsing bottom-up


      Ciò si riflette in una definizione multipla per una entry
  ◮
      della tabella di parsing:
                                       Simbolo di input
           N.T.
                      i         t       e            a     b      $
                  S → iEtSS ′                    S→a
            S
                                     ′
                                    S → eS
           S′                                                   S′ → ǫ
                                     S′ → ǫ
                                                          E→b
            E




      Tuttavia se, con in input il carattere e, il parser
  ◮
      venisse “istruito” a scegliere sempre la produzione
      S ′ → eS, l’ambiguità si risolverebbe (e pure con
      l’intepretazione “naturale”, che associa ogni else al
      then più vicino).
LFC
Implementazione non ricorsiva
                                                                 Parsing di
                                                                 linguaggi liberi
      È possibile dare un’implementazione non ricorsiva di
  ◮                                                              Introduzione
                                                                 Parsing top-down
      un parser predittivo (cioè che non richiede                Parsing bottom-up


      backtracking) mantenendo esplicitamente una pila.
      La pila serve per memorizzare i simboli della parte
  ◮
      destra della produzione scelta.
      Tali simboli verranno poi “confrontati” con l’input (se
  ◮
      terminali) o ulteriormente riscritti (se non terminali).
      Il comportamento del parser può essere descritto nel
  ◮
      modo seguente.
      Inizialmente, sullo stack sono contenuti (partendo dal
  ◮
      fondo) i simboli $ ed S, mentre un opportuno
      puntatore (diciamo z) punta al primo carattere di
      input.
LFC
Implementazione non ricorsiva (continua)
                                                                      Parsing di
                                                                      linguaggi liberi
                                                                      Introduzione
                                                                      Parsing top-down
      Al generico passo, il parser controlla il simbolo X
  ◮                                                                   Parsing bottom-up

      sulla testa dello stack;
            se X è un nonterminale e M[X , z] = ‘X → X1 . . . Xk ‘,
        ◮

            esegue una pop dallo stack (rimuove cioè X ) seguita
            da k push dei simboli X1 , . . . , Xk , nell’ordine;
            se X è un nonterminale e M[X , z] = ‘error ‘, segnala
        ◮

            una condizione di errore;
            se X è un terminale e X = z, esegue una pop e fa
        ◮

            avanzare z;
            se X è un terminale e X = z, segnala una
        ◮

            condizione di errore.
      Le operazioni terminano quando X = $.
  ◮
LFC
Esempio
                                                                                     Parsing di
                                                                                     linguaggi liberi
                                                                                     Introduzione

     Consideriamo nuovamente la grammatica delle
 ◮                                                                                   Parsing top-down
                                                                                     Parsing bottom-up

     espressioni con precedenza di operatore, della quale
     ricordiamo la tabella di parsing:

                                         Simbolo di input
      N.T.
                           (         )                +           ×
                id                                                            $
             E → TE ′   E → TE ′
      E
      E′                           E′ → ǫ       E ′ → +TE ′                 E′ → ǫ
             T → FT ′   T → FT ′
      T
      T′                           T′ → ǫ         T′ → ǫ      T ′ → ×FT ′   T′ → ǫ
             F → id     F → (E)
      F



     Supponendo di avere la stringa id + id in input, la
 ◮
     seguente tabella illustra il progressivo contenuto
     dello stack e dell’input.
LFC
Esempio (continua)
                                       Parsing di
                                       linguaggi liberi
              Stack            Input   Introduzione

              $E            id + id$   Parsing top-down
                                       Parsing bottom-up

              $E ′ T        id + id$
              $E ′ T ′ F    id + id$
              $E ′ T ′ id   id + id$
              $E ′ T ′         +id$
              $E ′             +id$
              $E ′ T +         +id$
              $E ′ T             id$
              $E ′ T ′ F         id$
              $E ′ T ′ id        id$
              $E ′ T ′             $
              $E ′                 $
              $                    $
LFC
Esercizi proposti
                                                               Parsing di
                                                               linguaggi liberi
      Si calcolini gli insiemi FIRST e FOLLOW per la
  ◮
                                                               Introduzione

      seguente grammatica:                                     Parsing top-down
                                                               Parsing bottom-up




                       S → c | AS | BS
                       A → aB | ǫ
                       B → bA | ǫ

      e si costruisca la relativa tabella di parsing per un
      parser a discesa ricorsiva.
      Si calcolino gli insiemi FIRST e FOLLOW per la
  ◮
      grammatica G2 , che descrive le espressioni regolari
      su {0, 1}, dopo averla modificata in modo da
      eliminare i prefissi comuni. Se ne costruisca quindi la
      tabella di parsing per un parser a discesa ricorsiva.
LFC
Linguaggi formali e compilazione
                                   Parsing di
                                   linguaggi liberi
                                   Introduzione
                                   Parsing top-down
                                   Parsing bottom-up




Parsing di linguaggi liberi
   Introduzione
   Parsing top-down
   Parsing bottom-up
LFC
Elementi generali
      Un parser generico di tipo bottom-up procede              Parsing di
  ◮
                                                                linguaggi liberi
      cercando di operare riduzioni, operazioni che             Introduzione
                                                                Parsing top-down
      possiamo vedere come l’inverso delle derivazioni, a       Parsing bottom-up


      partire dalla stringa di input α0 = α.
      Al generico passo di riduzione il parser individua,
  ◮
      nella stringa corrente αi , un’opportuna sottostringa β
      che corrisponde alla parte destra di una produzione
      A → β e sostituisce β con A, così riducendo αi ad
      αi+1 :
                     αi = γβδ,       αi+1 = γAδ
      Il processo termina con successo se, per un
  ◮
      opportuno valore di i, risulta αi = S.
      Nell’ambito del processo di riduzione il parser può
  ◮
      costruire (dal basso verso l’alto) un albero di
      derivazione e/o produrre codice.
LFC
Parsing “Shift-reduce”
                                                                          Parsing di
      Un parser shift-reduce è un parser di tipo bottom-up
  ◮                                                                       linguaggi liberi
      che fa uso di uno stack nel quale vengono                           Introduzione
                                                                          Parsing top-down

      memorizzati simboli (terminali o non terminali) della               Parsing bottom-up



      grammatica.
      Il nome deriva dal fatto che le due operazioni
  ◮
      fondamentali eseguite del parser sono dette,
      appunto, shift e riduzione.
            L’operazione di shift legge un simbolo dallo stream di
        ◮

            input e lo inserisce sullo stack.
            L’operazione di riduzione sostituisce sullo stack gli
        ◮

            ultimi k simboli inseriti (poniamo X1 , . . . , Xk ) con il
            simbolo A, naturalmente se esiste la produzione
            A → X1 . . . Xk .
      Le altre operazioni che il parser esegue sono
  ◮
      l’accettazione dell’input e la segnalazione di una
      condizione di errore.
LFC
Maniglie (handle)
                                                                  Parsing di
      Un parser di tipo shift-reduce deve individuare, come       linguaggi liberi
  ◮
                                                                  Introduzione

      sottostringhe da ridurre a non terminale, non già           Parsing top-down
                                                                  Parsing bottom-up

      qualunque sequenza che formi la parte destra di una
      produzione, bensì esattamente quelle sequenze (e
      quelle produzioni) usate nella derivazione canonica
      destra.
      Tali sequenze devono inoltre essere individuate
  ◮
      “nell’ordine giusto”, e cioè l’ordine rovesciato rispetto
      alla corrispondente derivazione canonica destra.
      Queste sequenze (ma meglio sarebbe dire
  ◮
      “produzioni”) vengono chiamate handle (maniglie), di
      modo che il problema centrale della realizzazione di
      un tale parser può essere espresso sinteticamente
      come il problema di individuare le maniglie.
LFC
Esempio
     Si consideri la grammatica:
 ◮
                                                               Parsing di
                                                               linguaggi liberi
                           → E +T | T
                       E                                       Introduzione
                                                               Parsing top-down

                           → T ×F | F
                       T                                       Parsing bottom-up



                           → id | (E )
                       F
     La corretta riduzione di id + id × id ad E individua le
 ◮
     maniglie indicate dalla sottolineatura:
                 id + id × id ⇐ F + id × id
                               ⇐ T + id × id
                               ⇐ E + id × id
                               ⇐ E + F × id
                               ⇐ E + T × id
                               ⇐ E +T ×F
                               ⇐ E +T
                               ⇐E
LFC
Maniglie (continua)
                                                                  Parsing di
      In un parser shift-reduce le successive stringhe αi         linguaggi liberi
  ◮
                                                                  Introduzione

      (che rappresentano la trasformazione dall’input             Parsing top-down
                                                                  Parsing bottom-up

      all’assioma) si trovano “parte sullo stack e parte
      ancora sullo stream di input“.
      In particolare, non sarebbe difficile dimostrare che,
  ◮
      se il parser ricostruisce (in ordine inverso) derivazioni
      canoniche destre, la maniglie appaiono sempre sulla
      cima dello stack.
      Ad esempio, se αi = γβδ e β è una maniglia, allora
  ◮
      γβ sta sullo stack (nell’ordine, dal fondo verso la
      cima) mentre δ sta ancora nello stream di input.
      La maniglia, in questo caso β, sta sulla cima dello
  ◮
      stack e viene “sostituita” con A dall’operazione di
      riduzione.
LFC
Esempio
     Azioni eseguite (su input id + id × id) da un parser
 ◮
                                                             Parsing di
     shift-reduce che riconosce correttamente le maniglie.   linguaggi liberi
                                                             Introduzione

      Stack                 Input   Azione   Stringa αi      Parsing top-down
                                                             Parsing bottom-up

                    id + id × id$            id + id × id$
      $                             shift
                       +id × id$             id + id × id$
      $id                           reduce
                       +id × id$             F + id × id$
      $F                            reduce
                       +id × id$             T + id × id$
      $T                            reduce
                       +id × id$             E + id × id$
      $E                            shift
                         id × id$            E + id × id$
      $E +                          shift
                            ×id$             E + id × id$
      $E + id                       reduce
                            ×id$             E + F × id$
      $E + F                        reduce
                            ×id$             E + T × id$
      $E + T                        shift
      $E + T ×                               E + T × id$
                              id$   shift
      $E + T × id                            E + T × id$
                                $   reduce
      $E + T × F                             E + T × F$
                                $   reduce
      $E + T                    $            E + T$
                                    reduce
      $E                        $            E$
                                    accept
LFC
Esempio (continua)
                                                              Parsing di
      Si noti che, nella colonna azione, bisognerebbe
  ◮
                                                              linguaggi liberi
      correttamente indicare, nel caso di riduzione, quale    Introduzione
                                                              Parsing top-down

      produzione va utilizzata.                               Parsing bottom-up



      In generale, la stessa parte destra potrebbe infatti
  ◮
      corrispondere a più di una produzione (non è questo
      il caso), ed è per questo motivo che si definiscono le
      handle come produzioni e non come semplici
      sequenze.
      Si noti inoltre come le maniglie “corrette”
  ◮
      (corrispondenti alla derivazione canonica destra)
      appaiano effettivamente sempre sulla cima dello
      stack.
      Questa proprietà non varrebbe nel caso volessimo
  ◮
      riprodurre, poniamo, una derivazione canonica
      sinistra.
LFC
Il cuore computazionale del problema
                                                                 Parsing di
                                                                 linguaggi liberi
      La difficoltà di progettazione del parser sta tutta nella
  ◮                                                              Introduzione
                                                                 Parsing top-down
      capacità di riconoscere quando è corretto operare          Parsing bottom-up


      uno shift e quando invece è corretto operare una
      riduzione.
      Il problema coincide con quello di determinare
  ◮
      esattamente le maniglie. Infatti, se fossimo in grado
      di risolvere quest’ultimo sapremmo sempre quando
      “shiftare” e quando “ridurre”.
      Dovremmo infatti ridurre quando e solo quando una
  ◮
      maniglia appare sulla cima dello stack.
      Sfortunatamente ci sono grammatiche per le quali il
  ◮
      paradigma shift-reduce non è applicabile, ad
      esempio grammatiche ambigue.
LFC
Esercizi proposti
                                                                   Parsing di
                                                                   linguaggi liberi
      Per la grammatica                                            Introduzione
  ◮                                                                Parsing top-down
                                                                   Parsing bottom-up


                          S → (S)S | ǫ

      si tracci (sulla falsariga di quanto visto nell’esempio
      di slide 48) il comportamento di un parser
      shift-reduce su input (()()) e ()(()), nell’ipotesi che il
      parser sia in grado di individuare correttamente le
      maniglie.
      Si verifichi l’esistenza di un conflitto shift-reduce per
  ◮
      la grammatica che astrae il problema del
      dangling-else nei linguaggi di programmazione (slide
      35)
LFC
Parser LR
                                                               Parsing di
                                                               linguaggi liberi
      Si tratta di una classe di parser di tipo shift-reduce
  ◮                                                            Introduzione
                                                               Parsing top-down
      (con analisi dell’input da sinistra a destra, “Left to   Parsing bottom-up


      Right”), caratterizzati da una struttura di programma
      comune ma con capacità di analisi diverse.
      La diversa capacità di effettuare il parsing dipende
  ◮
      dall’informazione contenuta in apposite tabelle di
      parsing che guidano il comportamento del
      programma.
      In questi appunti analizzeremo un solo tipo di parser
  ◮
      LR, il più semplice, che prende (non a caso) il nome
      di (parser) SLR(1).
      Per prima cosa vedremo però la “program structure”
  ◮
      comune.
LFC
Struttura di un parser LR
      Un parser LR è caratterizzato da un programma di         Parsing di
  ◮
                                                               linguaggi liberi
      controllo (essenzialmente un automa a stati finiti) che   Introduzione
                                                               Parsing top-down

      ha accesso ad uno stack e ad una tabella di parsing,     Parsing bottom-up


      oltre che a opportuni supporti di input e output.
      Le tabelle prescrivono il comportamento del
  ◮
      programma di controllo esclusivamente in funzione
      del contenuto dello stack e dei primi k caratteri
      presenti in input.
      Il valore di k è uno dei parametri che rende più o
  ◮
      meno potente il parser (nel seguito vedremo solo il
      caso k = 1)
      Lo stack, a differenza dei parser shift-reduce visti
  ◮
      precedentemente, contiene stati anziché simboli.
      Tuttavia, come vedremo, ad ogni stato è associato
      univocamente un simbolo della grammatica.
LFC
Tabelle di parsing
                                                                  Parsing di
                                                                  linguaggi liberi
      Le tabelle di parsing di un parser LR hanno un
  ◮                                                               Introduzione

      numero di righe pari al numero di stati dell’automa         Parsing top-down
                                                                  Parsing bottom-up

      che costituisce il controllo.
      Le colonne sono indicizzate dai simboli terminali e
  ◮
      non terminali. Le colonne relative ai terminali
      formano quella che viene detta “parte ACTION” della
      tabella, mentre le altre formano la “parte GOTO”.
      Nella parte action sono previste 4 tipi di azioni:
  ◮
            avanzamento di un carattere sullo stream di input e
        ◮

            inserimento di uno stato in cima allo stack;
            utilizzo di una riduzione;
        ◮

            accettazione dell’input;
        ◮

            rilevamento di un errore.
        ◮


      La parte GOTO prescrive stati da inserire nello stack.
  ◮
LFC
Funzionamento del parser
      Il funzionamento del parser è definito come segue.
  ◮
                                                                Parsing di
                                                                linguaggi liberi
      Inizialmente, lo stack contiene un solo stato (lo stato
  ◮                                                             Introduzione
                                                                Parsing top-down
      iniziale, naturalmente).                                  Parsing bottom-up


      Al generico passo, sia q lo stato in cima allo stack e
  ◮
      x il prossimo carattere in input.
      Se ACTION [q, x] = shift r , il parser avanza il
  ◮
      puntatore di input e inserisce lo stato r sullo stack.
      Se ACTION [q, x] = reduce i, il parser utilizza la
  ◮
      i-esima produzione (secondo una numerazione
      arbitraria ma prefissata). Più precsamente, se A → α
      è tale produzione, il parser rimuove ki = |α| stati
      dallo stack e vi inserisce lo stato GOTO [q ′ , A] dove
      q ′ è lo stato sulla cima dello stack dopo le ki
      rimozioni.
      Il parser si arresta (naturalmente) in seguito ad
  ◮
      accettazione o errore.
LFC
Esempio
     Consideriamo la grammatica che genera sequenze         Parsing di
 ◮
                                                            linguaggi liberi
     di parentesi bilanciate:                               Introduzione
                                                            Parsing top-down
      S → (S)S            Produzione 1                      Parsing bottom-up


      S→ǫ                 Produzione 2
     e consideriamo la sguente tabella di parsing (di cui
     vedremo più avanti la costruzione):
                              ACTION            GOTO
          Stato
                    (           )       $        S
            0     shift 2   reduce 2 reduce 2     1
            1                         accept
            2     shift 2   reduce 2 reduce 2    3
            3                shift 4
            4     shift 2   reduce 2 reduce 2    5
            5               reduce 1 reduce 1
     Consideriamo il comportamento del parser su input
 ◮
     ()().
LFC
Esempio (continua)
           Stack         Input    Azione
                                                               Parsing di
           $0             ()()$   shift 2                      linguaggi liberi
                                                               Introduzione
                                             →ǫ
           $02             )()$   reduce S                     Parsing top-down
                                                               Parsing bottom-up
           $023            )()$   shift 4
           $0234            ()$   shift 2
                                             →ǫ
           $02342            )$   reduce S
           $023423           )$   shift 4
                                             →ǫ
           $0234234           $   reduce S
                                             → (S)S
           $02342345          $   reduce S
                                             → (S)S
           $02345             $   reduce S
           $01                $   accept

      Si ricordi che la riduzione con S → (S)S prima
  ◮
      rimuove 4 stati dallo stack, quindi inserisce lo stato
      GOTO[q ′ , S], dove q ′ è lo stato che rimane in cima
      allo stack dopo le rimozioni.
      Analogamente, la riduzione con S → ǫ rimuove 0
  ◮
      stati.
LFC
Parsing SLR(1)
                                                                Parsing di
                                                                linguaggi liberi
                                                                Introduzione

      Il primo tipo di parser LR che analizziamo è detto        Parsing top-down
  ◮
                                                                Parsing bottom-up

      Simple LR parser (o semplicemente SLR).
      È caratterizzato da tabelle di parsing di relativamente
  ◮
      semplice costruzione (da cui il nome) ma che danno
      minori garanzie sulla possibilità di analisi di
      grammatiche libere.
      In altri termini, ci sono diverse grammatiche libere di
  ◮
      interesse che non possono essere analizzate con
      parser SLR (e, segnatamente, SLR(1)).
      Si tratta comunque di un primo caso di interesse per
  ◮
      capire la “logica” di un parser LR.
LFC
Automa LR(0)
      Il passo fondamentale consiste nella definizione di
  ◮
                                                               Parsing di
      un automa (che di fatto sarà poi “trasferito” nella      linguaggi liberi
                                                               Introduzione

      tabella di parsing), detto automa LR(0).                 Parsing top-down
                                                               Parsing bottom-up

      Data G (la grammatica) si procede innanzitutto
  ◮
      inserendo una produzione aggiuntiva, S ′ → S (il cui
      significato sarà chiaro più avanti).
      A partire dalle produzioni, si definiscono poi speciali
  ◮
      “oggetti”, che chiameremo item (usando la
      terminologia standard inglese).
      Un item è una produzione con inserito un marcatore
  ◮
      nella parte destra, tipicamente un punto.
      Ad esempio, gli item associati alla produzione
  ◮
      S → (S)S sono: S → ·(S)S, S → (·S)S, S → (S·)S,
      S → (S) · S e S → (S)S·.
      Ad una produzione tipo S → ǫ è associato il solo
  ◮
      item S → ·.
LFC
Automa LR(0) (continua)
                                                                 Parsing di
                                                                 linguaggi liberi
      Il significato intuitivo di un item associato ad una
  ◮                                                              Introduzione
                                                                 Parsing top-down
      produzione è di indicare in quale punto siamo arrivati     Parsing bottom-up


      nel processo di riconoscimento della parte destra
      della produzione stessa.
      Ad esempio, l’item S → (S) · S indica che abbiamo
  ◮
      riconosciuto una stringa descritta da (S) e che
      “speriamo” di riconoscere successivamente una
      stringa descrivibile da S.
      Gli item vengono poi raggruppati in insiemi (gruppi o
  ◮
      collezioni) che descrivono essenzialmente le stesse
      situazioni relative all’avanzamento del processo di
      parsing.
      Gli insiemi di item costituiranno gli stati dell’automa.
  ◮
LFC
Automa LR(0) (continua)
      Con riferimento all’item S → (S) · S, se siamo al
  ◮                                                             Parsing di
                                                                linguaggi liberi
      punto da esso descritto (cioè, in particolare, al punto   Introduzione
                                                                Parsing top-down
      in cui speriamo di vedere una stringa descrivibile da     Parsing bottom-up


      S) possiamo dire di essere anche al punto descritto
      dai seguenti due item: → ·(S)S e S → ·.
      Questo naturalmente perché il non terminale S (che
  ◮
      speriamo di vedere) può assumere le due forme
      (S)S e ǫ.
      Si noti che l’intersezione di due collezioni può non
  ◮
      essere vuota.
      Ad esempio, l’item S → (·S)S forma gruppo ancora
  ◮
      con S → ·(S)S e S → · (per lo stesso motivo).
      Sono le collezioni nel loro insieme che devono
  ◮
      essere distinte.
      Si noti che l’item S → (S·)S forma un gruppo da solo.
  ◮
LFC
Esempio
     Per la grammatica “aumentata”
 ◮                                                    Parsing di
                                                      linguaggi liberi
                                                      Introduzione

                        S′ → S                        Parsing top-down
                                                      Parsing bottom-up


                        S → (S)S | ǫ

     sono definiti i seguenti insiemi di item:

            I0 : S ′ → ·S          I3 : S → (S·)S
                 S → ·(S)S
                 S→·               I4 : S → (S) · S
                                        S → ·(S)S
            I1 : S ′ → S·               S→·

            I2 : S → (·S)S         I5 : S → (S)S·
                 S → ·(S)S
                 S→·
LFC
Come costruire gli insiemi LR(0)
      Diamo ora una descrizione dettagliata del
  ◮
                                                                 Parsing di
      procedimento di costruzione degli insiemi di item.         linguaggi liberi
                                                                 Introduzione
                                                                 Parsing top-down
      L’insieme iniziale (che indicheremo sempre con I0 )
  ◮                                                              Parsing bottom-up


      contiene l’item S ′ → ·S e tutti gli item ottenuti dalle
      produzioni di S inserendo il punto all’inizio.
      Nell’esempio appena considerato, si aggiungono a
  ◮
      S ′ → ·S due soli item (perché ci sono due produzioni
      relative ad S).
      Si procede poi iterativamente, lavorando ad ogni
  ◮
      passo su un insieme Ij già formato.
      Si considerano tutti i simboli della grammatica
  ◮
      immediatamente alla destra del punto in item di Ij .
      Per ogni simbolo così individuato, si forma un gruppo
  ◮
      Ik che contiene, inizialmente, gli item ottenuti
      spostando il punto alla destra del simbolo
      considerato.
LFC
Come costruire gli insiemi LR(0) (continua)
                                                                Parsing di
                                                                linguaggi liberi
      Ad esempio, fra gli item di I0 (per la grammatica
  ◮                                                             Introduzione
                                                                Parsing top-down
      appena considerata) ci sono due soli simboli alla         Parsing bottom-up


      destra del punto, S e ( . Per ognuno di essi si creano
      due nuovi insiemi, I1 e I2 , che contengono
      inizialmente un solo item ciascuno, S ′ → S· in I1 e
      S → (·S)S in I2 .
      Tornando alla descrizione generale, se il nuovo
  ◮
      insieme Ik appena inizializzato contiene item in cui il
      punto precede un simbolo non terminale A , si
      aggiungono ad Ik tutti gli item ottenuti dalle
      produzioni di A inserendo il punto all’inizio.
      Quest’ultima operazione, che abbiamo già visto più
  ◮
      volte, è detta chiusura dell’insieme Ik .
LFC
Come costruire gli insiemi LR(0) (continua)
                                                              Parsing di
                                                              linguaggi liberi
                                                              Introduzione
                                                              Parsing top-down
                                                              Parsing bottom-up

      Ad esempio, ad I2 l’operazione di chiusura aggiunge
  ◮
      gli item S → ·(S)S e S → ·.
      Ad I1 invece non viene aggiunto nulla, perché l’unico
  ◮
      item iniziale (S ′ → S·) non ha simboli non terminali
      immediatamente a destra del punto.
      Se Ik non coincide con nessuno degli insiemi già
  ◮
      generati viene tenuto, altrimenti viene scartato.
      Il procedimento si arresta quando non ci sono più
  ◮
      insiemi di item da considerare.
LFC
Esempio
     Consideriamo la seguente grammatica (aumentata)
 ◮                                                            Parsing di
                                                              linguaggi liberi
     che genera il linguaggio {an bn |n ≥ 1}:                 Introduzione
                                                              Parsing top-down
                                                              Parsing bottom-up
                             ′
                                 →S
                         S
                         S → aSb | ab

     L’insieme iniziale di item è:
 ◮
           I0 = {S ′ → ·S, S → ·aSb, S → ·ab}.
       ◮

     I simboli immediatamente a destra del punto in I0
 ◮
     sono S e a, per cui inizializziamo due insiemi:
           I1 = {S ′ → S·};
       ◮

           I2 = {S → a · Sb, S → a · b}.
       ◮

     La chiusura non aggiunge nulla a I1 (poiché l’item
 ◮
     S ′ → ·S ha un non terminale immediatamente a
     destra del punto), mentre aggiunge item ad I2 , che
     diventa
           I2 = {S → a · Sb, S → a · b, S → ·aSb, S → ·ab}.
       ◮
LFC
Esempio (continua)
                                                                 Parsing di
                                                                 linguaggi liberi
                                                                 Introduzione
      L’insieme non dà origine ad altri insiemi di item
  ◮                                                              Parsing top-down
                                                                 Parsing bottom-up
      (perché non ci sono simboli a destra del punto).
      Nel’insieme I2 ci sono tre simboli distinti a destra del
  ◮
      punto, per cui formiamo tre insiemi:
            I3 = {S → aS · b};
        ◮

            I4 = {S → ab·};
        ◮

            I5 = {S → a · Sb, S → a · b}.
        ◮

      La chiusura modifica solo I5 , che diviene
  ◮
            I5 = {S → a · Sb, S → a · b, S → ·aSb, S → ·ab},
        ◮

      ma che viene scartato in quanto coincidente con I2 .
      Infine lavorando su I3 si ottiene:
  ◮
            I5 = {S → aSb·},
        ◮
LFC
Esempio (continua)
                                                                Parsing di
                                                                linguaggi liberi
      Ricapitolando, gli insiemi LR(0) di item associati alla
  ◮                                                             Introduzione
                                                                Parsing top-down

      grammatica sono:                                          Parsing bottom-up




             I0 : S ′ → ·S           I3 : S → aS · b
                  S → ·aSb
                  S → ·ab            I4 : S → ab·

             I1 : S ′ → S·           I5 : S → aSb·

                      → a · Sb
             I2 : S
                      →a·b
                  S
                      → ·aSb
                  S
                      → ·ab
                  S
LFC
Funzioni CLOSURE e GOTO
                                                                    Parsing di
                                                                    linguaggi liberi
     Il prodedimento appena descritto (in maniera
 ◮                                                                  Introduzione
                                                                    Parsing top-down
     alquanto discorsiva) può essere sinteticamente                 Parsing bottom-up


     ricapitolato facendo uso delle due funzioni
     CLOSURE e GOTO, che lavorano su insiemi di item.
     Dato un insieme di item I, CLOSURE (I) coincide con
 ◮
     l’insieme I cui siano stati aggiunti tutti gli item del tipo
     B → ·β (dove B → β è una produzione della
     grammatica) qualora almeno un item di X sia del tipo
     A → α · Bγ.
     Se I è un insieme di item e X un simbolo della
 ◮
     grammatica GOTO(I, X ) è definito come la chiusura
     dell’insieme J di item del tipo A → αX · β, dove
     A → α · X β è in I.
LFC




                                                           Parsing di
                                                           linguaggi liberi
                                                           Introduzione
                                                           Parsing top-down
                                                           Parsing bottom-up


Algorithm 2 Insiemi di item LR(0)
 1: C ← {CLOSURE ({S ′ → S})}
 2: repeat
       for each I ∈ C do
 3:
          for each X ∈ T ∪ N do
 4:
             if GOTO(I, X ) = {}and GOTO(I, X ) ∈ C then
 5:
                 C ← C ∪ {GOTO(I, X )}
 6:
 7: until No new state is added to C
LFC
Esempio
                                                            Parsing di
                                                            linguaggi liberi
                                                            Introduzione
     Da ultimo, consideriamo la costruzione degli insiemi
 ◮                                                          Parsing top-down
                                                            Parsing bottom-up
     di item LR(0) per la grammatica aumentata

                      E′ → E
                           → E +T | T
                       E
                           → T ×F | F
                       T
                           → (E ) | id
                       F

     che, ricordiamo, non è adatta al parsing top-down.
     Nella slide seguente presentiamo direttamente la
 ◮
     collezione degli insiemi di item ottenuta applicando
     l’algoritmo 2.
LFC
Esempio (continua)
                                                          Parsing di
                                                          linguaggi liberi
 I0 : E ′ → ·E              → (·E )    I7 : T → T × ·F
                   I4 : F                                  Introduzione
                                                           Parsing top-down
      E → ·E + T            → ·E + T        F → · (E )
                        E                                  Parsing bottom-up


      E → ·T                → ·T            F → ·id
                        E
      T → ·T × F            → ·T × F
                        T
      T → ·F                → ·F       I8 : E → E · +T
                        T
      F → · (E )            → · (E )        F → (E ·)
                        F
      F → ·id               → ·id
                        F
                                       I9 : E → E + T ·
 I1 : E ′ → E ·    I5 : F → id·             T → T · ×F
      E → E · +T
                            → E + ·T   I10 : T → T × F ·
                   I6 : E
 I2 : E → T ·               → ·T × F
                        T
      T → T · ×F            → ·F       I11 : F → (E ) ·
                        T
                            → · (E )
                        F
 I3 : T → F ·               → ·id
                        F
LFC
Automa LR(0)
                                                              Parsing di
                                                              linguaggi liberi
      Come già anticipato, le collezioni di item LR(0)
  ◮                                                           Introduzione
                                                              Parsing top-down
      determinate con la procedura appena descritta           Parsing bottom-up

      costituiscono gli stati dell’automa LR(0) (che, a sua
      volta, è alla base del parsing SLR(1) che stiamo
      costruendo).
      Per completare la descrizione dell’automa è
  ◮
      necessario definire la funzione δ di transizione.
      In realtà abbiamo già descritto tale funzione, che
  ◮
      coincide “essenzialmente” con la funzione GOTO.
      Si noti che, tuttavia, che GOTO(I, X ) “costruisce”
  ◮
      nuovi stati e dunque J = GOTO(I, X ) non viene
      aggiunto se risulta già definito,
      In tale caso vale comunqe δ(I, X ) = J.
  ◮
LFC
Esempio
                                                                 Parsing di
     L’automa LR(0) per la grammatica                            linguaggi liberi
 ◮
                                                                 Introduzione
                                                                 Parsing top-down
                                                                 Parsing bottom-up
                             ′
                                  →S
                         S
                         S → (S)S | ǫ

     è:
                                      accept
                             I1
                                  $
                 S
                                                        S
            I0                                 I4           I5
                     (                (

                             I2                     )
                                      S

                             (                 I3
LFC
Esempio
                                                                    Parsing di
     L’automa LR(0) per la grammatica                               linguaggi liberi
 ◮
                                                                    Introduzione
                                                                    Parsing top-down
                                                                    Parsing bottom-up
                            ′
                                    →S
                        S
                        S → aSb | ab

     è:
                                             accept
                                    I1
                                         $
                    S

                                                           b
               I0                                     I3       I5
                                         S
                        a

                                             b
                                    I2                I4
                                a
LFC
Esempio
      L’ultimo esempio è per la grammatica
 ◮
                                              ′                                                   Parsing di
                                                   →
                                          E             E
                                                                                                  linguaggi liberi
                                                   →    E +T | T
                                              E
                                                                                                  Introduzione
                                                   →    T ×F | F
                                              T                                                   Parsing top-down
                                                                                                  Parsing bottom-up
                                                   →    (E) | id
                                              F
              accept
                         $
                             +                                      T
                       I1            I6

                                 F
                                                            +
      E
                       I3                                                                   I9
                                              id
          F
                                     (
                                                                              )
                  id
     I0                                                                  I8       I11
                                                       I5
                             F            id
                                                                                        ×
              (                                                     id
      T                                            F
                                     I4
                             T                                  (
                                          (
                                                                                  F
                                                                                            I10
                       I2                                                I7
                                          ×
LFC
Tabelle di parsing SLR(1)
                                                             Parsing di
                                                             linguaggi liberi
                                                             Introduzione

      Completiamo ora la descrizione del parser con          Parsing top-down
  ◮
                                                             Parsing bottom-up

      l’algoritmo di definizione della tabella di parsing.
      Le tabelle incorporano le informazioni contenute
  ◮
      nell’automa, che da sono non è sufficiente per
      eseguire l’analisi (si ricordi che un automa a stati
      finiti non è in grado di riconoscere linguaggi liberi
      (che non siano anche regolari).
      L’algoritmo esamina gli stati dell’automa e le
  ◮
      transizioni uscendi da ciascuno stato.
      Esso necessita anche di conoscere, per ogni simbolo
  ◮
      non terminale A, l’insieme di simboli FOLLOW (A).
LFC
Tabelle di parsing SLR(1) (continua)
      Per ogni stato Ij , consideriamo le transizioni uscenti.
  ◮
                                                                 Parsing di
                                                                 linguaggi liberi
      Se esiste una transizione da Ij a Ik etichettata X ∈ T
  ◮                                                              Introduzione

      poniamo ACTION [j, X ] = shift k.                          Parsing top-down
                                                                 Parsing bottom-up


      Se esiste una transizione etichettata X ∈ N poniamo
  ◮
      GOTO [j, X ] = shift X .
      Se nell’insieme di item corrispondenti a Ij esiste un
  ◮
      item A → α·, allora poniamo
      ACTION [j, X ] = reduce A → α per tutti i simboli X in
      FOLLOW (A).
      Se Ij contiene l’item S ′ → S· si pone
  ◮
      ACTION[i, $] = accept.
      Se, ad un qualunque passo dell’algoritmo, si
  ◮
      manifesta un cosiddetto conflitto shft-reduce (cioè si
      tenta di inserire in una entry della parte ACTION sia
      un’azione di shift che una di riduzione) allora la
      grammatica non è SLR(1).
LFC
Esempio
                                                         Parsing di
                                                         linguaggi liberi
                                                         Introduzione
     Per la grammatica
 ◮                                                       Parsing top-down
                                                         Parsing bottom-up
       S → aSb          Produzione 1
       S → ab           Produzione 2
     l’algoritmo appena delineato produce la seguente
     tabella:
                             ACTION               GOTO
          Stato
                                          $        S
                    a         b
           0      shift 2                           1
           1                           accept
           2      shift 2    shift 4               3
           3                 shift 5
           4                reduce 2   reduce 2
           5                reduce 1   reduce 1
LFC
Esempio (continua)
                                                          Parsing di
                                                          linguaggi liberi
                                                          Introduzione
                                                          Parsing top-down
      Consideriamo il comportamento del parser su input
  ◮                                                       Parsing bottom-up


      aabb

            Stack    Input   Azione
            $0               shift 2
                    aabb$
            $02              shift 2
                     abb$
            $022             shift 4
                      bb$
                             reduce S → ab
            $0224       b$
            $023             shift 5
                        b$
                             reduce S → aSb
            $0235        $
            $01          $   accept
LFC
Esempio
     Diamo infine la tabella di parsing per la grammatica
 ◮
                                                                   Parsing di
      E → E + T Prod. 1              T→F           Prod. 4         linguaggi liberi
                                                                   Introduzione
      E→T                            F → (E ) Prod. 5
                       Prod. 2                                     Parsing top-down
                                                                   Parsing bottom-up
      T → T × F Prod. 3              F → id        Prod. 6
                            ACTION                       GOTO
      Stato
                        ×
                   +           (      )
              id                              $      E    T   F
        0     s5              s4                     1    2   3
        1          s6                       accept
        2          r2   s7           r2       r2
        3          r4   r4           r4       r4
        4     s5              s4                     8    2   3
        5          r6   r6           r6      r6
        6     s5              s4                          9    3
        7     s5              s4                              10
        8          s6                s 11
        9          r1   s7            r1     r1
       10          r3   r3            r3     r3
       11          r5   r5            r5     r5
LFC
Esempio (continua)
      Consideriamo il comportamento del parser su input
  ◮
                                                                 Parsing di
      id × (id + id)                                             linguaggi liberi
                                                                 Introduzione
                                                                 Parsing top-down
          Stack                      Input    Azione             Parsing bottom-up

          $0               id × (id + id) $   shift 5
          $0 5                × (id + id) $   reduce F   → id
          $0 3                × (id + id) $   reduce T   →F
          $0 2                × (id + id) $   shift 7
          $0 2 7                (id + id) $   shift 4
          $0 2 7 4               id + id) $   shift 5
          $0 2 7 4 5                +id) $    reduce F   → id
          $0 2 7 4 3                +id) $    reduce T   →F
          $0 2 7 4 2                +id) $    reduce E   →T
          $0 2 7 4 8                +id) $    shift 6
          $0 2 7 4 8 6                id) $   shift 5
          $0 2 7 4 8 6 5                )$    reduce F   → id
          $0 2 7 4 8 6 3                )$    reduce T   →F
          $0 2 7 4 8 6 9                )$               →E +T
                                              reduce E
          $0 2 7 4 8                    )$    shift 11
          $0 2 7 4 8 11                   $              → (E)
                                              reduce F
          $0 2 7 10                       $   reduce T   →T ×F
          $0 2                            $   reduce E   →T
          $0 1                            $   accept

More Related Content

What's hot

系統程式 -- 前言
系統程式 -- 前言系統程式 -- 前言
系統程式 -- 前言
鍾誠 陳鍾誠
 
IMP questions for System programming for GTU
IMP questions for System programming for GTUIMP questions for System programming for GTU
IMP questions for System programming for GTU
Paras Patel
 
系統程式 -- 第 6 章
系統程式 -- 第 6 章系統程式 -- 第 6 章
系統程式 -- 第 6 章
鍾誠 陳鍾誠
 
Chapter 1 1
Chapter 1 1Chapter 1 1
Chapter 1 1bolovv
 
Compiler
CompilerCompiler
系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言
鍾誠 陳鍾誠
 
系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器
鍾誠 陳鍾誠
 
系統程式 -- 第 6 章 巨集處理器
系統程式 -- 第 6 章 巨集處理器系統程式 -- 第 6 章 巨集處理器
系統程式 -- 第 6 章 巨集處理器
鍾誠 陳鍾誠
 
系統程式 - 第二章
系統程式 - 第二章系統程式 - 第二章
系統程式 - 第二章
鍾誠 陳鍾誠
 
Compiler Design
Compiler DesignCompiler Design
Compiler DesignMir Majid
 
what is compiler and five phases of compiler
what is compiler and five phases of compilerwhat is compiler and five phases of compiler
what is compiler and five phases of compiler
adilmehmood93
 
系統程式 -- 第 10 章
系統程式 -- 第 10 章系統程式 -- 第 10 章
系統程式 -- 第 10 章
鍾誠 陳鍾誠
 
系統程式 -- 第 10 章 作業系統
系統程式 -- 第 10 章 作業系統系統程式 -- 第 10 章 作業系統
系統程式 -- 第 10 章 作業系統
鍾誠 陳鍾誠
 
Programming languages
Programming languagesProgramming languages
Programming languages
Dr. B T Sampath Kumar
 
系統程式 -- 附錄
系統程式 -- 附錄系統程式 -- 附錄
系統程式 -- 附錄
鍾誠 陳鍾誠
 
Lecture 01 introduction to compiler
Lecture 01 introduction to compilerLecture 01 introduction to compiler
Lecture 01 introduction to compiler
Iffat Anjum
 
Nlp
NlpNlp
COMPILER DESIGN- Introduction & Lexical Analysis:
COMPILER DESIGN- Introduction & Lexical Analysis: COMPILER DESIGN- Introduction & Lexical Analysis:
COMPILER DESIGN- Introduction & Lexical Analysis:
Jyothishmathi Institute of Technology and Science Karimnagar
 
Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...
Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...
Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...butest
 
Introduction to scripts
Introduction to scriptsIntroduction to scripts
Introduction to scripts
sana mateen
 

What's hot (20)

系統程式 -- 前言
系統程式 -- 前言系統程式 -- 前言
系統程式 -- 前言
 
IMP questions for System programming for GTU
IMP questions for System programming for GTUIMP questions for System programming for GTU
IMP questions for System programming for GTU
 
系統程式 -- 第 6 章
系統程式 -- 第 6 章系統程式 -- 第 6 章
系統程式 -- 第 6 章
 
Chapter 1 1
Chapter 1 1Chapter 1 1
Chapter 1 1
 
Compiler
CompilerCompiler
Compiler
 
系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言
 
系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器系統程式 -- 第 4 章 組譯器
系統程式 -- 第 4 章 組譯器
 
系統程式 -- 第 6 章 巨集處理器
系統程式 -- 第 6 章 巨集處理器系統程式 -- 第 6 章 巨集處理器
系統程式 -- 第 6 章 巨集處理器
 
系統程式 - 第二章
系統程式 - 第二章系統程式 - 第二章
系統程式 - 第二章
 
Compiler Design
Compiler DesignCompiler Design
Compiler Design
 
what is compiler and five phases of compiler
what is compiler and five phases of compilerwhat is compiler and five phases of compiler
what is compiler and five phases of compiler
 
系統程式 -- 第 10 章
系統程式 -- 第 10 章系統程式 -- 第 10 章
系統程式 -- 第 10 章
 
系統程式 -- 第 10 章 作業系統
系統程式 -- 第 10 章 作業系統系統程式 -- 第 10 章 作業系統
系統程式 -- 第 10 章 作業系統
 
Programming languages
Programming languagesProgramming languages
Programming languages
 
系統程式 -- 附錄
系統程式 -- 附錄系統程式 -- 附錄
系統程式 -- 附錄
 
Lecture 01 introduction to compiler
Lecture 01 introduction to compilerLecture 01 introduction to compiler
Lecture 01 introduction to compiler
 
Nlp
NlpNlp
Nlp
 
COMPILER DESIGN- Introduction & Lexical Analysis:
COMPILER DESIGN- Introduction & Lexical Analysis: COMPILER DESIGN- Introduction & Lexical Analysis:
COMPILER DESIGN- Introduction & Lexical Analysis:
 
Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...
Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...
Part-of-Speech Tagging for Bengali Thesis submitted to Indian ...
 
Introduction to scripts
Introduction to scriptsIntroduction to scripts
Introduction to scripts
 

More from Majong DevJfu

9 - Architetture Software - SOA Cloud
9 - Architetture Software - SOA Cloud9 - Architetture Software - SOA Cloud
9 - Architetture Software - SOA CloudMajong DevJfu
 
8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processes8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processesMajong DevJfu
 
7 - Architetture Software - Software product line
7 - Architetture Software - Software product line7 - Architetture Software - Software product line
7 - Architetture Software - Software product lineMajong DevJfu
 
6 - Architetture Software - Model transformation
6 - Architetture Software - Model transformation6 - Architetture Software - Model transformation
6 - Architetture Software - Model transformationMajong DevJfu
 
5 - Architetture Software - Metamodelling and the Model Driven Architecture
5 - Architetture Software - Metamodelling and the Model Driven Architecture5 - Architetture Software - Metamodelling and the Model Driven Architecture
5 - Architetture Software - Metamodelling and the Model Driven ArchitectureMajong DevJfu
 
4 - Architetture Software - Architecture Portfolio
4 - Architetture Software - Architecture Portfolio4 - Architetture Software - Architecture Portfolio
4 - Architetture Software - Architecture PortfolioMajong DevJfu
 
3 - Architetture Software - Architectural styles
3 - Architetture Software - Architectural styles3 - Architetture Software - Architectural styles
3 - Architetture Software - Architectural stylesMajong DevJfu
 
2 - Architetture Software - Software architecture
2 - Architetture Software - Software architecture2 - Architetture Software - Software architecture
2 - Architetture Software - Software architectureMajong DevJfu
 
1 - Architetture Software - Software as a product
1 - Architetture Software - Software as a product1 - Architetture Software - Software as a product
1 - Architetture Software - Software as a productMajong DevJfu
 
10 - Architetture Software - More architectural styles
10 - Architetture Software - More architectural styles10 - Architetture Software - More architectural styles
10 - Architetture Software - More architectural stylesMajong DevJfu
 

More from Majong DevJfu (20)

9 - Architetture Software - SOA Cloud
9 - Architetture Software - SOA Cloud9 - Architetture Software - SOA Cloud
9 - Architetture Software - SOA Cloud
 
8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processes8 - Architetture Software - Architecture centric processes
8 - Architetture Software - Architecture centric processes
 
7 - Architetture Software - Software product line
7 - Architetture Software - Software product line7 - Architetture Software - Software product line
7 - Architetture Software - Software product line
 
6 - Architetture Software - Model transformation
6 - Architetture Software - Model transformation6 - Architetture Software - Model transformation
6 - Architetture Software - Model transformation
 
5 - Architetture Software - Metamodelling and the Model Driven Architecture
5 - Architetture Software - Metamodelling and the Model Driven Architecture5 - Architetture Software - Metamodelling and the Model Driven Architecture
5 - Architetture Software - Metamodelling and the Model Driven Architecture
 
4 - Architetture Software - Architecture Portfolio
4 - Architetture Software - Architecture Portfolio4 - Architetture Software - Architecture Portfolio
4 - Architetture Software - Architecture Portfolio
 
3 - Architetture Software - Architectural styles
3 - Architetture Software - Architectural styles3 - Architetture Software - Architectural styles
3 - Architetture Software - Architectural styles
 
2 - Architetture Software - Software architecture
2 - Architetture Software - Software architecture2 - Architetture Software - Software architecture
2 - Architetture Software - Software architecture
 
1 - Architetture Software - Software as a product
1 - Architetture Software - Software as a product1 - Architetture Software - Software as a product
1 - Architetture Software - Software as a product
 
10 - Architetture Software - More architectural styles
10 - Architetture Software - More architectural styles10 - Architetture Software - More architectural styles
10 - Architetture Software - More architectural styles
 
Uml3
Uml3Uml3
Uml3
 
Uml2
Uml2Uml2
Uml2
 
6
66
6
 
5
55
5
 
4 (uml basic)
4 (uml basic)4 (uml basic)
4 (uml basic)
 
3
33
3
 
2
22
2
 
1
11
1
 
Tmd template-sand
Tmd template-sandTmd template-sand
Tmd template-sand
 
26 standards
26 standards26 standards
26 standards
 

Linguaggi Formali e Compilazione: Parsing

  • 1. LFC Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Linguaggi formali e compilazione Corso di Laurea in Informatica A.A. 2008/2009
  • 2. LFC Linguaggi formali e compilazione Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up
  • 3. LFC Linguaggi formali e compilazione Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up
  • 4. LFC Parsing di linguaggi liberi Ogni linguaggio libero può essere riconosciuto in ◮ Parsing di linguaggi liberi tempo proporzionale al cubo della lunghezza della Introduzione stringa in input. Parsing top-down Parsing bottom-up L’algoritmo che stabilisce questo limite di ◮ complessità, basato su una classica applicazione della programmazione dinamica, è tuttavia inutilizzabile in applicazioni reali. Gli algoritmi di parsing utilizzati nella pratica sono ◮ generalmente classificati base alla strategia utilizzata per la costruzione (anche solo “ideale”) del parse-tree, top-down o bottom-up. All’interno di queste due categorie generali, gli ◮ algoritmi si differenziano in base a scelte strategiche più dettagliate. Tali algoritmi non possono analizzare tutti i linguaggi ◮ context-free.
  • 5. LFC Tipi di parser Parsing di linguaggi liberi Introduzione Parsing top-down In concreto, i tipi di parser più diffusi includono: ◮ Parsing bottom-up parser a discesa ricorsiva (top-down), ◮ parser di tipo LR (bottom-up), ◮ parser a precedenza di operatori (bottom-up). ◮ Nel parsing top-down si cerca una derivazione ◮ canonica per la stringa/programma in input partendo dall’assioma iniziale. La costruzione top-down dell’albero di derivazione ◮ corrisponde in modo naturale al riconoscimento di un blocco/procedura/espressione in termini delle parti costituenti.
  • 6. LFC Tipi di parser (continua) Parsing di linguaggi liberi Introduzione Parsing top-down Nel parsing bottom-up si cerca una derivazione Parsing bottom-up ◮ canonica per la stringa/programma in input partendo dalla stringa stessa e applicando riduzioni successive. Una riduzione non è nient’altro che l’applicazione “in ◮ senso contrario” di una produzione della grammatica. La costruzione bottom-up dell’albero di derivazione ◮ corrisponde in modo naturale al riconoscimento di singole porzioni di un programma e alla loro composizione in parti più complesse.
  • 7. LFC Non determinismo Parsing di linguaggi liberi Introduzione In entrambi i tipi di parser (top-down o bottom-up) è ◮ Parsing top-down Parsing bottom-up possibile (anche se non desiderabile) che la scelta della produzione da utilizzare, in una delle due direzioni, non sia determinabile con certezza. Se la scelta di una produzione è “errata” il ◮ riconoscimento fallisce. In tal caso, prima di dichiarare che la stringa è ◮ sintatticamente errata, è necessario operare un backtracking sull’input, ritornando al punto di scelta. Per i parser a discesa ricorsiva (e implementati ◮ mediante ricorsione) ciò può essere sufficientemente agevole, anche se computazionalmente pesante.
  • 8. LFC Linguaggi formali e compilazione Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up
  • 9. LFC Parsing a discesa ricorsiva (recursive descent) Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Un parser a discesa ricorsiva (d.r.) può essere ◮ realizzato come collezione di procedure (mutuamente) ricorsive, una per ogni simbolo non terminale della grammatica. Siano A → α1 . . . αk le produzioni relative al ◮ i i nonterminale A, e sia αi = X1 . . . Xni , i = 1, . . . , k. La procedura relativa al simbolo A è illustrata nel ◮ seguente algoritmo, in cui (per semplicità di notazione) evitiamo i doppi indici e supponiamo che il simbolo corrente di input sia contenuto nella variabile globale x.
  • 10. LFC Parsing di linguaggi liberi Introduzione Parsing top-down Algorithm 1 Procedura per il nonterminale A Parsing bottom-up → 1: Scegli “opportunamente” una produzione A X1 X2 . . . Xk (Xj ∈ V) 2: for j = 1, . . . , k do if Xj ∈ N then 3: Xj () 4: else if Xj = x then 5: x ← read() 6: else 7: error() 8:
  • 11. LFC Backtracking La scelta della produzione da applicare (riga 1 Parsing di ◮ linguaggi liberi dell’algoritmo 1) può essere meno critica se, nel caso Introduzione Parsing top-down di fallimento, viene effettuato backtracking sull’input. Parsing bottom-up L’algoritmo deve essere modificato in modo tale da ◮ dichiarare fallimento solo quando tutte le produzioni per il nonterminale A sono state provate senza successo. Deve però essere disposto un meccanismo per ◮ riposizionare il puntatore di input su quello che era il simbolo corrente al momento della chiamata della procedura. Ne consegue che: o il parser bufferizza l’input ◮ ricevuto dallo scanner, oppure è quest’ultimo che deve essere opportunamente programmato per la ripetizione di certe sequenze di token.
  • 12. LFC Scelta della produzione Fortunatamente, in molti casi concreti il backtracking ◮ Parsing di linguaggi liberi non è necessario perché la scelta della produzione Introduzione Parsing top-down può essere fatta con lookahead limitato (cioè Parsing bottom-up guardando pochi token a partire dalla posizione corrente di input). Consideriamo la semplice grammatica: ◮ A → aA | bB B → ǫ | bB Per entrambi i non terminali, la scelta della ◮ produzione da usare può essere fatta guardando il prossimo simbolo x in input. Per A: se x = a, usa la produzione A → aA; se ◮ x = b, usa la produzione A → bB. Per B. se x = $ (end of input), usa la produzione ◮ B → ǫ; se x = b, usa la produzione B → bB;
  • 13. LFC Grammatiche inadatte al parsing a discesa ricorsiva Parsing di Alcune produzioni sono inadatte al r.d. parsing. linguaggi liberi ◮ Introduzione Produzioni con ricorsioni a sinistra, che possono ◮ Parsing top-down Parsing bottom-up causare cicli infiniti, come nella grammatica → E +T | T E → T ∗F | F T → id | (E ) F Produzioni con prefissi comuni (come il noto caso del ◮ “dangling else”), che rendono non limitabile a priori la quantità di lookahead. Un altro esempio (artificiale) è A → aB | aC B → aB | b C → aC | c Come sappiamo, esistono opportune trasformazioni ◮ per risolvere questi problemi.
  • 14. LFC FIRST e FOLLOW Data una grammatica G, l’implementazione di parser ◮ Parsing di a d.r. per G utilizza (secondo le “teorie” più accettate) linguaggi liberi Introduzione due funzioni, dette FIRST e FOLLOW , che Parsing top-down Parsing bottom-up determinano la scelta della produzione da utilizzare. Data G = (N , T , P, S) e data una stringa ◮ α ∈ (N ∪ T )∗ , si definisce FIRST (α) l’insieme dei simboli terminali con cui può iniziare una frase derivabile da α, più eventualmente ǫ se α ⇒∗ ǫ: FIRST (α) = {x ∈ T |α ⇒∗ xβ, β ∈ T ∗ } ∪ {ǫ} , se α ⇒∗ ǫ. Per ogni nonterminale A ∈ N si definisce ◮ FOLLOW (A) l’insieme dei terminali che si possono trovare immediatamente alla destra di A in una qualche forma di frase. In altri termini, x ∈ FOLLOW (A) se S ⇒∗ αAxβ, con α e β opportuni.
  • 15. LFC Calcolo di FIRST (α) Parsing di linguaggi liberi Introduzione Definiamo innanzitutto come si calcola FIRST (α) nel ◮ Parsing top-down Parsing bottom-up caso in cui α sia un singolo simbolo della grammatica, cioè α = X con X ∈ N ∪ T . Se X è un terminale, si pone naturalmente ◮ FIRST (X ) = {X }; se X è un nonterminale poniamo innanzitutto ◮ FIRST (X ) = {}. Se esiste la produzione X → X1 . . . Xn , e risulta ◮ ǫ ∈ FIRST (Xj ), j = 1, . . . , k − 1, poniamo FIRST (X ) = FIRST (X ) ∪ {x} per ogni simbolo x ∈ FIRST (Xk ). Infine, se esiste la produzione X → ǫ oppure ◮ ǫ ∈ FIRST (Xj ), j = 1, . . . , k , poniamo FIRST (X ) = FIRST (X ) ∪ {ǫ}.
  • 16. LFC Esempi Si consideri nuovamente la grammatica ◮ Parsing di linguaggi liberi ′ ′ → (E ) E | id E E Introduzione Parsing top-down Parsing bottom-up E′ → + E E′ | × E E′ | ǫ Per questa grammatica risulta ◮ FIRST (E) = {( , id} ◮ FIRST (E ′ ) = {+, ×, ǫ} ◮ Si consideri ora la grammatica per an bm ck ◮ A → aA | BC | ǫ B → bB | ǫ C → cC | ǫ Per questa grammatica risulta ◮ FIRST (C) = {c, ǫ} ◮ FIRST (B) = {b, ǫ} ◮ FIRST (A) = {a, b, c, ǫ} ◮
  • 17. LFC Esempio Parsing di linguaggi liberi Introduzione Parsing top-down Si riconsideri la grammatica per le espressioni che ◮ Parsing bottom-up “forza” la precedenza di operatori: → E +T | T E → T ×F | F T → id | (E ) F Per questa grammatica risulta ◮ FIRST (F ) = {( , id}; ◮ FIRST (T ) = FIRST (F ); ◮ FIRST (E) = FIRST (T ). ◮
  • 18. LFC Calcolo di FIRST (α) (continua) Parsing di linguaggi liberi Introduzione Il calcolo di FIRST (α), dove α = X1 . . . Xn è una Parsing top-down ◮ Parsing bottom-up generica stringa di terminali e nonterminali, può ora essere svolto nel modo seguente a ∈ FIRST (α) se e solo se, per qualche indice ◮ k ∈ 1, . . . n − 1, risulta a ∈ Xk e ǫ ∈ Xj , j = 1, . . . , k − 1 (si suppone sempre ǫ ∈ FIRST (X0)). Se inoltre ǫ ∈ FIRST (Xj ), j = 1, . . . , n, allora ◮ ǫ ∈ FIRST (α). Ad esempio, nel caso della seconda grammatica del ◮ lucido precedente risulta: FIRST (aA) = {a} e FIRST (BC) = {b, c, ǫ}.
  • 19. LFC Calcolo di FOLLOW (A) Parsing di linguaggi liberi Introduzione Il calcolo di FOLLOW (A), per un generico non Parsing top-down ◮ Parsing bottom-up terminale A, può essere svolto in questo modo. Se A = S, si inserisce il simbolo speciale $ ◮ (marcatore di fine input) in FOLLOW (A). Se esiste la produzione B → αAβ, tutti i terminali in ◮ FIRST (β) si inseriscono in FOLLOW (A); inoltre, se ǫ ∈ FIRST (β), tutti i terminali che stanno in FOLLOW (B) si inseriscono in FOLLOW (A) . Analogamente, se esiste la produzione B → αA, tutti ◮ i terminali che stanno in FOLLOW (B) si inseriscono in FOLLOW (A) .
  • 20. LFC Esempio Parsing di Consideriamo la solita grammatica ◮ linguaggi liberi Introduzione Parsing top-down ′ ′ → (E ) E | id E E Parsing bottom-up ′ ′ ′ → +E E | × E E | ǫ E Possiamo subito stabilire che FOLLOW (E ) include il ◮ simbolo $ e il simbolo ); inoltre contiene i simboli in FIRST (E ′) (eccetto ǫ) e cioè + e ×. FOLLOW (E ′) include FOLLOW (E ), a causa (ad ◮ esempio) della produzione E → idE ′ . La produzione E ′ → +EE ′ , unitamente a E ′ → ǫ, ◮ stabilisce che vale anche il contrario, e cioè che FOLLOW (E ) include FOLLOW (E ′ ). Mettendo tutto insieme si ottiene ◮ FOLLOW (E ) = FOLLOW (E ′) = {$, ) , +, ×}.
  • 21. LFC Esempio Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Per la grammatica ◮ A → aA | BC | ǫ B → bB | ǫ C → cC | ǫ risulta FOLLOW (A) = FOLLOW (C) = {$} e FOLLOW (B) = {c, $}.
  • 22. LFC Esempio Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Per la grammatica con precedenza di operatori: ◮ → E +T | T E → T ×F | F T → id | (E ) F risulta: FOLLOW (E) = {$, +, )}; ◮ FOLLOW (T ) = FOLLOW (E) ∪ {×}; ◮ FOLLOW (F ) = FOLLOW (T ). ◮
  • 23. LFC Grammatiche LL(1) Per opportune classi di grammatiche libere è ◮ Parsing di possibile realizzare parser a d.r. che non richiedono linguaggi liberi Introduzione backtracking (tali parser sono anche detti predittivi). Parsing top-down Parsing bottom-up Una tale classe di grammatiche è la LL(1), dove le ◮ due L stanno per “left-to-right” e “leftmost derivation”. In altri termini, il parsing avviene leggendo l’input ◮ unidirezionalmente, da sinistra a destra, e costruendo una derivazione canonica sinistra della stringa. Il parametro 1 indica che è richiesto un solo simbolo di lookahead. La classe LL(1) è in grado di esprimere molte fra le ◮ caratteristiche della sintassi dei linguaggi di programmazione. Nessuna grammatica ricorsiva a sinistra o con ◮ produzioni aventi previssi comuni può però essere LL(1) (come pure nessuna grammatica ambigua).
  • 24. LFC Grammatiche LL(1) (continua) Parsing di linguaggi liberi Come possiamo stabilire se una grammatica è ◮ Introduzione Parsing top-down LL(1)? Parsing bottom-up Consideriamo la procedura per un generico ◮ nonterminale A. Se esistono due produzioni A → α e A → β, allora ◮ certamente non deve accadere che FIRST (α) ∩ FIRST (β) = {} , perché in tal caso un solo carattere di lookahead non basterebbe per decidere correttamente la produzione da usare. Tuttavia ciò potrebbe non essere sufficiente. ◮
  • 25. LFC Grammatiche LL(1) (continua) Parsing di linguaggi liberi ⇒∗ Supponiamo (ad esempio) che α ǫ. Potremmo ◮ Introduzione Parsing top-down trovarci nella seguente situazione: Parsing bottom-up S ⇒∗ γAxδ, γ ∈ T ∗, x ∈ T , δ ∈ V ∗ dove, se il prossimo carattere di input fosse proprio x, potrebbe essere corretto usare la produzione A → α. Tuttavia, se x fosse anche in FIRST (β) potrebbe ◮ essere corretto pure usare la produzione A → β. Deve quindi risultare α ⇒∗ ǫ implica ◮ FOLLOW (A) ∩ FIRST (β) = {} e, analogamente, β ⇒∗ ǫ implica FOLLOW (A) ∩ FIRST (α) = {}
  • 26. LFC Esempio Si consideri la grammatica ◮ Parsing di linguaggi liberi A → BE Introduzione Parsing top-down B → C|D Parsing bottom-up C → ǫ | cc D → ǫ | dd → c|d E Poiché risulta FIRST (C) ∩ FIRST (D) = {ǫ}, la ◮ grammatica non è LL(1). Infatti, supponiamo che la stringa in input inizi con c. ◮ Dopo la riscrittura dell’assioma il parser verrebbe a trovarsi con la forma di frase BE e il carattere c in input. A questo punto potrebbe essere corretto derivare ◮ tanto CE (se l’input fosse, ad esempio, ccd) quanto DE (se l’input fosse c).
  • 27. LFC Esempio Si modifichi la precedente grammatica nel modo ◮ Parsing di seguente linguaggi liberi Introduzione Parsing top-down Parsing bottom-up A → BE B → C|D C → ǫ | cc D → dd → c|d E (cancellando cioè la produzione D → ǫ). Poiché risulta FIRST (D) ∩ FOLLOW (B) = {d}, la ◮ grammatica non è LL(1). Il problema si verifica con input che inizia con d, ◮ perché potrebbe essere corretto (dopo la derivazione iniziale) derivare tanto CE (se l’input fosse d) quanto DE (se l’input fosse, ad esempio ddc).
  • 28. LFC Costruzione della tabella di parsing Parsing di Per una grammatica LL(1) è possibile costruire linguaggi liberi ◮ Introduzione (utilizzando le funzioni FIRST e FOLLOW ) una Parsing top-down Parsing bottom-up tabella, detta tabella di parsing, che, per ogni nonterminale A e ogni terminale x, prescrive il comportamento del parser. Indicheremo con MG la tabella di parsing relativa alla ◮ grammatica G (o semplicemente con M, se la grammatica è evidente). La generica entry MG [A, x] della tabella può ◮ contenere una produzione A → α di G, oppure essere vuota, ad indicare che si è verificato un errore. Disponendo di MG , la prima riga dell’algoritmo 1 ◮ viene sostituita da un lookup a MG [A, x].
  • 29. LFC Costruzione della tabella di parsing (continua) Parsing di linguaggi liberi Introduzione Parsing top-down L’algoritmo di costruzione della parsing table è molto ◮ Parsing bottom-up semplice ed è formato da un ciclo principale nel quale si prendono in considerazione tutte le produzioni. Per ogni produzione A → α: ◮ per ogni simbolo x in FIRST (α) si pone ◮ M[A, x ] = ‘A → α′ ; se ǫ ∈ FIRST (α), per ogni simbolo y in FOLLOW (A) ◮ si pone M[A, y ] = ‘A → α′ . Tutte le altre entry della tabella vengono lasciate ◮ vuote (ad indicare l’occorrenza di un errore). Se la grammatica è LL(1), nessuna entry della ◮ tabella viene riempita con più di una produzione.
  • 30. LFC Esempio Consideriamo ancora la grammatica con precedenza ◮ Parsing di di operatori: linguaggi liberi Introduzione → E +T | T E Parsing top-down Parsing bottom-up → T ×F | F T → id | (E ) F Tale grammatica (pur non ambigua) non è LL(1) ◮ perchè (p.e.), entrambe le produzioni E → E + T e E → T verrebbero poste nella entry M[E , id]. Eliminando la left-recursion si può però ottenere una ◮ grammatica equivalente che è LL(1): → TE ′ E E ′ → +TE ′ | ǫ → FT ′ T T ′ → ×FT ′ | ǫ → id | (E ) F
  • 31. LFC Esempio (continua) Per la grammatica modificata ◮ Parsing di linguaggi liberi ′ → TE E Introduzione Parsing top-down Parsing bottom-up E ′ → +TE ′ | ǫ → FT ′ T T ′ → ×FT ′ | ǫ → id | (E ) F abbiamo: FIRST (F ) = FIRST (T ) = FIRST (E) = {id, ( }; ◮ FIRST (E ′ ) = {+, ǫ}; ◮ FIRST (T ′ ) = {×, ǫ}; ◮ FOLLOW (E) = FOLLOW (E ′ ) = {$, )}; ◮ FOLLOW (T ) = (FIRST (E ′ ) {ǫ}) ∪ FOLLOW (E ′ ) = ◮ {+, $, )}; FOLLOW (T ′ ) = FOLLOW (T ) = {+, $, )}; ◮ FOLLOW (F ) = (FIRST (T ′ ) {ǫ}) ∪ FOLLOW (T ′ ) = ◮ {×, +, $, )}.
  • 32. LFC Esempio (continua) Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up L’algoritmo produce la seguente tabella di parsing ◮ Simbolo di input N.T. ( ) + × id $ E → TE ′ E → TE ′ E E′ ′ ′ ′ ′ E →ǫ E → +TE E →ǫ T → FT ′ T → FT ′ T T′ T′ → ǫ T′ → ǫ T ′ → ×FT ′ T′ → ǫ F → id F → (E) F in cui le entry vuote corrispondono ad una situazione di errore.
  • 33. LFC Esempio Se tentassimo di produrre la tabella di parsing per la ◮ Parsing di linguaggi liberi grammatica Introduzione Parsing top-down Parsing bottom-up ′ ′ → (E ) E | id E E E′ → + E E′ | × E E′ | ǫ otterremmo Simbolo di input N.T. ( ) + × id $ E → idE ′ E → (E)E ′ E E ′ → +EE ′ E ′ → ×EE ′ E′ E′ → ǫ E′ → ǫ E′ → ǫ E′ → ǫ Con in input il carattere + o il carattere ×, il parser ◮ non saprebbe quindi come procedere. Il non soddisfacimento delle proprietà LL(1) è una ◮ conseguenza dell’ambiguità della grammatica.
  • 34. LFC Esempio (continua) Parsing di linguaggi liberi Introduzione In questo caso è comunque possibile forzare nel ◮ Parsing top-down Parsing bottom-up parser una regola di scelta senza compromettere la capacità di riconoscere il linguaggio generato dalla grammatica. Possiamo (anzi, dobbiamo!) semplicemente optare ◮ in favore delle produzioni E ′ → +EE ′ e E ′ → ×EE ′ , anziché E ′ → ǫ. Si noti comunque che, pur avendo risolto l’ambiguità, ◮ l’interpretazione delle espressioni che deriva dall’albero di parsing non è quella “naturale” (non viene soddisfatta la precedenza naturale degli operatori).
  • 35. LFC Esempio Consideriamo la seguente grammatica: ◮ Parsing di linguaggi liberi S → iE tS eS | iE tS | a Introduzione Parsing top-down →b E Parsing bottom-up che mette a fuoco il problema del dangling else di molti linguaggi e che, avendo produzioni con un prefisso comune, non è idonea al parsing a d.r. Operando nel modo già illustrato, otteniamo: ◮ S → i E t S S′ | a S′ → e S | ǫ →b E e risulta FIRST (S) = {i, a}; ◮ FIRST (S ′ ) = {e, ǫ}; ◮ FIRST (E) = {b}; ◮ FOLLOW (S) = FOLLOW (S ′ ) = {$, e}. ◮
  • 36. LFC Esempio (continua) Parsing di La grammatica modificata è non è ancora LL(1) in linguaggi liberi ◮ Introduzione quanto FIRST (eS) ∩ FOLLOW (S ′ ) = {e}. Parsing top-down Parsing bottom-up Ciò si riflette in una definizione multipla per una entry ◮ della tabella di parsing: Simbolo di input N.T. i t e a b $ S → iEtSS ′ S→a S ′ S → eS S′ S′ → ǫ S′ → ǫ E→b E Tuttavia se, con in input il carattere e, il parser ◮ venisse “istruito” a scegliere sempre la produzione S ′ → eS, l’ambiguità si risolverebbe (e pure con l’intepretazione “naturale”, che associa ogni else al then più vicino).
  • 37. LFC Implementazione non ricorsiva Parsing di linguaggi liberi È possibile dare un’implementazione non ricorsiva di ◮ Introduzione Parsing top-down un parser predittivo (cioè che non richiede Parsing bottom-up backtracking) mantenendo esplicitamente una pila. La pila serve per memorizzare i simboli della parte ◮ destra della produzione scelta. Tali simboli verranno poi “confrontati” con l’input (se ◮ terminali) o ulteriormente riscritti (se non terminali). Il comportamento del parser può essere descritto nel ◮ modo seguente. Inizialmente, sullo stack sono contenuti (partendo dal ◮ fondo) i simboli $ ed S, mentre un opportuno puntatore (diciamo z) punta al primo carattere di input.
  • 38. LFC Implementazione non ricorsiva (continua) Parsing di linguaggi liberi Introduzione Parsing top-down Al generico passo, il parser controlla il simbolo X ◮ Parsing bottom-up sulla testa dello stack; se X è un nonterminale e M[X , z] = ‘X → X1 . . . Xk ‘, ◮ esegue una pop dallo stack (rimuove cioè X ) seguita da k push dei simboli X1 , . . . , Xk , nell’ordine; se X è un nonterminale e M[X , z] = ‘error ‘, segnala ◮ una condizione di errore; se X è un terminale e X = z, esegue una pop e fa ◮ avanzare z; se X è un terminale e X = z, segnala una ◮ condizione di errore. Le operazioni terminano quando X = $. ◮
  • 39. LFC Esempio Parsing di linguaggi liberi Introduzione Consideriamo nuovamente la grammatica delle ◮ Parsing top-down Parsing bottom-up espressioni con precedenza di operatore, della quale ricordiamo la tabella di parsing: Simbolo di input N.T. ( ) + × id $ E → TE ′ E → TE ′ E E′ E′ → ǫ E ′ → +TE ′ E′ → ǫ T → FT ′ T → FT ′ T T′ T′ → ǫ T′ → ǫ T ′ → ×FT ′ T′ → ǫ F → id F → (E) F Supponendo di avere la stringa id + id in input, la ◮ seguente tabella illustra il progressivo contenuto dello stack e dell’input.
  • 40. LFC Esempio (continua) Parsing di linguaggi liberi Stack Input Introduzione $E id + id$ Parsing top-down Parsing bottom-up $E ′ T id + id$ $E ′ T ′ F id + id$ $E ′ T ′ id id + id$ $E ′ T ′ +id$ $E ′ +id$ $E ′ T + +id$ $E ′ T id$ $E ′ T ′ F id$ $E ′ T ′ id id$ $E ′ T ′ $ $E ′ $ $ $
  • 41. LFC Esercizi proposti Parsing di linguaggi liberi Si calcolini gli insiemi FIRST e FOLLOW per la ◮ Introduzione seguente grammatica: Parsing top-down Parsing bottom-up S → c | AS | BS A → aB | ǫ B → bA | ǫ e si costruisca la relativa tabella di parsing per un parser a discesa ricorsiva. Si calcolino gli insiemi FIRST e FOLLOW per la ◮ grammatica G2 , che descrive le espressioni regolari su {0, 1}, dopo averla modificata in modo da eliminare i prefissi comuni. Se ne costruisca quindi la tabella di parsing per un parser a discesa ricorsiva.
  • 42. LFC Linguaggi formali e compilazione Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up
  • 43. LFC Elementi generali Un parser generico di tipo bottom-up procede Parsing di ◮ linguaggi liberi cercando di operare riduzioni, operazioni che Introduzione Parsing top-down possiamo vedere come l’inverso delle derivazioni, a Parsing bottom-up partire dalla stringa di input α0 = α. Al generico passo di riduzione il parser individua, ◮ nella stringa corrente αi , un’opportuna sottostringa β che corrisponde alla parte destra di una produzione A → β e sostituisce β con A, così riducendo αi ad αi+1 : αi = γβδ, αi+1 = γAδ Il processo termina con successo se, per un ◮ opportuno valore di i, risulta αi = S. Nell’ambito del processo di riduzione il parser può ◮ costruire (dal basso verso l’alto) un albero di derivazione e/o produrre codice.
  • 44. LFC Parsing “Shift-reduce” Parsing di Un parser shift-reduce è un parser di tipo bottom-up ◮ linguaggi liberi che fa uso di uno stack nel quale vengono Introduzione Parsing top-down memorizzati simboli (terminali o non terminali) della Parsing bottom-up grammatica. Il nome deriva dal fatto che le due operazioni ◮ fondamentali eseguite del parser sono dette, appunto, shift e riduzione. L’operazione di shift legge un simbolo dallo stream di ◮ input e lo inserisce sullo stack. L’operazione di riduzione sostituisce sullo stack gli ◮ ultimi k simboli inseriti (poniamo X1 , . . . , Xk ) con il simbolo A, naturalmente se esiste la produzione A → X1 . . . Xk . Le altre operazioni che il parser esegue sono ◮ l’accettazione dell’input e la segnalazione di una condizione di errore.
  • 45. LFC Maniglie (handle) Parsing di Un parser di tipo shift-reduce deve individuare, come linguaggi liberi ◮ Introduzione sottostringhe da ridurre a non terminale, non già Parsing top-down Parsing bottom-up qualunque sequenza che formi la parte destra di una produzione, bensì esattamente quelle sequenze (e quelle produzioni) usate nella derivazione canonica destra. Tali sequenze devono inoltre essere individuate ◮ “nell’ordine giusto”, e cioè l’ordine rovesciato rispetto alla corrispondente derivazione canonica destra. Queste sequenze (ma meglio sarebbe dire ◮ “produzioni”) vengono chiamate handle (maniglie), di modo che il problema centrale della realizzazione di un tale parser può essere espresso sinteticamente come il problema di individuare le maniglie.
  • 46. LFC Esempio Si consideri la grammatica: ◮ Parsing di linguaggi liberi → E +T | T E Introduzione Parsing top-down → T ×F | F T Parsing bottom-up → id | (E ) F La corretta riduzione di id + id × id ad E individua le ◮ maniglie indicate dalla sottolineatura: id + id × id ⇐ F + id × id ⇐ T + id × id ⇐ E + id × id ⇐ E + F × id ⇐ E + T × id ⇐ E +T ×F ⇐ E +T ⇐E
  • 47. LFC Maniglie (continua) Parsing di In un parser shift-reduce le successive stringhe αi linguaggi liberi ◮ Introduzione (che rappresentano la trasformazione dall’input Parsing top-down Parsing bottom-up all’assioma) si trovano “parte sullo stack e parte ancora sullo stream di input“. In particolare, non sarebbe difficile dimostrare che, ◮ se il parser ricostruisce (in ordine inverso) derivazioni canoniche destre, la maniglie appaiono sempre sulla cima dello stack. Ad esempio, se αi = γβδ e β è una maniglia, allora ◮ γβ sta sullo stack (nell’ordine, dal fondo verso la cima) mentre δ sta ancora nello stream di input. La maniglia, in questo caso β, sta sulla cima dello ◮ stack e viene “sostituita” con A dall’operazione di riduzione.
  • 48. LFC Esempio Azioni eseguite (su input id + id × id) da un parser ◮ Parsing di shift-reduce che riconosce correttamente le maniglie. linguaggi liberi Introduzione Stack Input Azione Stringa αi Parsing top-down Parsing bottom-up id + id × id$ id + id × id$ $ shift +id × id$ id + id × id$ $id reduce +id × id$ F + id × id$ $F reduce +id × id$ T + id × id$ $T reduce +id × id$ E + id × id$ $E shift id × id$ E + id × id$ $E + shift ×id$ E + id × id$ $E + id reduce ×id$ E + F × id$ $E + F reduce ×id$ E + T × id$ $E + T shift $E + T × E + T × id$ id$ shift $E + T × id E + T × id$ $ reduce $E + T × F E + T × F$ $ reduce $E + T $ E + T$ reduce $E $ E$ accept
  • 49. LFC Esempio (continua) Parsing di Si noti che, nella colonna azione, bisognerebbe ◮ linguaggi liberi correttamente indicare, nel caso di riduzione, quale Introduzione Parsing top-down produzione va utilizzata. Parsing bottom-up In generale, la stessa parte destra potrebbe infatti ◮ corrispondere a più di una produzione (non è questo il caso), ed è per questo motivo che si definiscono le handle come produzioni e non come semplici sequenze. Si noti inoltre come le maniglie “corrette” ◮ (corrispondenti alla derivazione canonica destra) appaiano effettivamente sempre sulla cima dello stack. Questa proprietà non varrebbe nel caso volessimo ◮ riprodurre, poniamo, una derivazione canonica sinistra.
  • 50. LFC Il cuore computazionale del problema Parsing di linguaggi liberi La difficoltà di progettazione del parser sta tutta nella ◮ Introduzione Parsing top-down capacità di riconoscere quando è corretto operare Parsing bottom-up uno shift e quando invece è corretto operare una riduzione. Il problema coincide con quello di determinare ◮ esattamente le maniglie. Infatti, se fossimo in grado di risolvere quest’ultimo sapremmo sempre quando “shiftare” e quando “ridurre”. Dovremmo infatti ridurre quando e solo quando una ◮ maniglia appare sulla cima dello stack. Sfortunatamente ci sono grammatiche per le quali il ◮ paradigma shift-reduce non è applicabile, ad esempio grammatiche ambigue.
  • 51. LFC Esercizi proposti Parsing di linguaggi liberi Per la grammatica Introduzione ◮ Parsing top-down Parsing bottom-up S → (S)S | ǫ si tracci (sulla falsariga di quanto visto nell’esempio di slide 48) il comportamento di un parser shift-reduce su input (()()) e ()(()), nell’ipotesi che il parser sia in grado di individuare correttamente le maniglie. Si verifichi l’esistenza di un conflitto shift-reduce per ◮ la grammatica che astrae il problema del dangling-else nei linguaggi di programmazione (slide 35)
  • 52. LFC Parser LR Parsing di linguaggi liberi Si tratta di una classe di parser di tipo shift-reduce ◮ Introduzione Parsing top-down (con analisi dell’input da sinistra a destra, “Left to Parsing bottom-up Right”), caratterizzati da una struttura di programma comune ma con capacità di analisi diverse. La diversa capacità di effettuare il parsing dipende ◮ dall’informazione contenuta in apposite tabelle di parsing che guidano il comportamento del programma. In questi appunti analizzeremo un solo tipo di parser ◮ LR, il più semplice, che prende (non a caso) il nome di (parser) SLR(1). Per prima cosa vedremo però la “program structure” ◮ comune.
  • 53. LFC Struttura di un parser LR Un parser LR è caratterizzato da un programma di Parsing di ◮ linguaggi liberi controllo (essenzialmente un automa a stati finiti) che Introduzione Parsing top-down ha accesso ad uno stack e ad una tabella di parsing, Parsing bottom-up oltre che a opportuni supporti di input e output. Le tabelle prescrivono il comportamento del ◮ programma di controllo esclusivamente in funzione del contenuto dello stack e dei primi k caratteri presenti in input. Il valore di k è uno dei parametri che rende più o ◮ meno potente il parser (nel seguito vedremo solo il caso k = 1) Lo stack, a differenza dei parser shift-reduce visti ◮ precedentemente, contiene stati anziché simboli. Tuttavia, come vedremo, ad ogni stato è associato univocamente un simbolo della grammatica.
  • 54. LFC Tabelle di parsing Parsing di linguaggi liberi Le tabelle di parsing di un parser LR hanno un ◮ Introduzione numero di righe pari al numero di stati dell’automa Parsing top-down Parsing bottom-up che costituisce il controllo. Le colonne sono indicizzate dai simboli terminali e ◮ non terminali. Le colonne relative ai terminali formano quella che viene detta “parte ACTION” della tabella, mentre le altre formano la “parte GOTO”. Nella parte action sono previste 4 tipi di azioni: ◮ avanzamento di un carattere sullo stream di input e ◮ inserimento di uno stato in cima allo stack; utilizzo di una riduzione; ◮ accettazione dell’input; ◮ rilevamento di un errore. ◮ La parte GOTO prescrive stati da inserire nello stack. ◮
  • 55. LFC Funzionamento del parser Il funzionamento del parser è definito come segue. ◮ Parsing di linguaggi liberi Inizialmente, lo stack contiene un solo stato (lo stato ◮ Introduzione Parsing top-down iniziale, naturalmente). Parsing bottom-up Al generico passo, sia q lo stato in cima allo stack e ◮ x il prossimo carattere in input. Se ACTION [q, x] = shift r , il parser avanza il ◮ puntatore di input e inserisce lo stato r sullo stack. Se ACTION [q, x] = reduce i, il parser utilizza la ◮ i-esima produzione (secondo una numerazione arbitraria ma prefissata). Più precsamente, se A → α è tale produzione, il parser rimuove ki = |α| stati dallo stack e vi inserisce lo stato GOTO [q ′ , A] dove q ′ è lo stato sulla cima dello stack dopo le ki rimozioni. Il parser si arresta (naturalmente) in seguito ad ◮ accettazione o errore.
  • 56. LFC Esempio Consideriamo la grammatica che genera sequenze Parsing di ◮ linguaggi liberi di parentesi bilanciate: Introduzione Parsing top-down S → (S)S Produzione 1 Parsing bottom-up S→ǫ Produzione 2 e consideriamo la sguente tabella di parsing (di cui vedremo più avanti la costruzione): ACTION GOTO Stato ( ) $ S 0 shift 2 reduce 2 reduce 2 1 1 accept 2 shift 2 reduce 2 reduce 2 3 3 shift 4 4 shift 2 reduce 2 reduce 2 5 5 reduce 1 reduce 1 Consideriamo il comportamento del parser su input ◮ ()().
  • 57. LFC Esempio (continua) Stack Input Azione Parsing di $0 ()()$ shift 2 linguaggi liberi Introduzione →ǫ $02 )()$ reduce S Parsing top-down Parsing bottom-up $023 )()$ shift 4 $0234 ()$ shift 2 →ǫ $02342 )$ reduce S $023423 )$ shift 4 →ǫ $0234234 $ reduce S → (S)S $02342345 $ reduce S → (S)S $02345 $ reduce S $01 $ accept Si ricordi che la riduzione con S → (S)S prima ◮ rimuove 4 stati dallo stack, quindi inserisce lo stato GOTO[q ′ , S], dove q ′ è lo stato che rimane in cima allo stack dopo le rimozioni. Analogamente, la riduzione con S → ǫ rimuove 0 ◮ stati.
  • 58. LFC Parsing SLR(1) Parsing di linguaggi liberi Introduzione Il primo tipo di parser LR che analizziamo è detto Parsing top-down ◮ Parsing bottom-up Simple LR parser (o semplicemente SLR). È caratterizzato da tabelle di parsing di relativamente ◮ semplice costruzione (da cui il nome) ma che danno minori garanzie sulla possibilità di analisi di grammatiche libere. In altri termini, ci sono diverse grammatiche libere di ◮ interesse che non possono essere analizzate con parser SLR (e, segnatamente, SLR(1)). Si tratta comunque di un primo caso di interesse per ◮ capire la “logica” di un parser LR.
  • 59. LFC Automa LR(0) Il passo fondamentale consiste nella definizione di ◮ Parsing di un automa (che di fatto sarà poi “trasferito” nella linguaggi liberi Introduzione tabella di parsing), detto automa LR(0). Parsing top-down Parsing bottom-up Data G (la grammatica) si procede innanzitutto ◮ inserendo una produzione aggiuntiva, S ′ → S (il cui significato sarà chiaro più avanti). A partire dalle produzioni, si definiscono poi speciali ◮ “oggetti”, che chiameremo item (usando la terminologia standard inglese). Un item è una produzione con inserito un marcatore ◮ nella parte destra, tipicamente un punto. Ad esempio, gli item associati alla produzione ◮ S → (S)S sono: S → ·(S)S, S → (·S)S, S → (S·)S, S → (S) · S e S → (S)S·. Ad una produzione tipo S → ǫ è associato il solo ◮ item S → ·.
  • 60. LFC Automa LR(0) (continua) Parsing di linguaggi liberi Il significato intuitivo di un item associato ad una ◮ Introduzione Parsing top-down produzione è di indicare in quale punto siamo arrivati Parsing bottom-up nel processo di riconoscimento della parte destra della produzione stessa. Ad esempio, l’item S → (S) · S indica che abbiamo ◮ riconosciuto una stringa descritta da (S) e che “speriamo” di riconoscere successivamente una stringa descrivibile da S. Gli item vengono poi raggruppati in insiemi (gruppi o ◮ collezioni) che descrivono essenzialmente le stesse situazioni relative all’avanzamento del processo di parsing. Gli insiemi di item costituiranno gli stati dell’automa. ◮
  • 61. LFC Automa LR(0) (continua) Con riferimento all’item S → (S) · S, se siamo al ◮ Parsing di linguaggi liberi punto da esso descritto (cioè, in particolare, al punto Introduzione Parsing top-down in cui speriamo di vedere una stringa descrivibile da Parsing bottom-up S) possiamo dire di essere anche al punto descritto dai seguenti due item: → ·(S)S e S → ·. Questo naturalmente perché il non terminale S (che ◮ speriamo di vedere) può assumere le due forme (S)S e ǫ. Si noti che l’intersezione di due collezioni può non ◮ essere vuota. Ad esempio, l’item S → (·S)S forma gruppo ancora ◮ con S → ·(S)S e S → · (per lo stesso motivo). Sono le collezioni nel loro insieme che devono ◮ essere distinte. Si noti che l’item S → (S·)S forma un gruppo da solo. ◮
  • 62. LFC Esempio Per la grammatica “aumentata” ◮ Parsing di linguaggi liberi Introduzione S′ → S Parsing top-down Parsing bottom-up S → (S)S | ǫ sono definiti i seguenti insiemi di item: I0 : S ′ → ·S I3 : S → (S·)S S → ·(S)S S→· I4 : S → (S) · S S → ·(S)S I1 : S ′ → S· S→· I2 : S → (·S)S I5 : S → (S)S· S → ·(S)S S→·
  • 63. LFC Come costruire gli insiemi LR(0) Diamo ora una descrizione dettagliata del ◮ Parsing di procedimento di costruzione degli insiemi di item. linguaggi liberi Introduzione Parsing top-down L’insieme iniziale (che indicheremo sempre con I0 ) ◮ Parsing bottom-up contiene l’item S ′ → ·S e tutti gli item ottenuti dalle produzioni di S inserendo il punto all’inizio. Nell’esempio appena considerato, si aggiungono a ◮ S ′ → ·S due soli item (perché ci sono due produzioni relative ad S). Si procede poi iterativamente, lavorando ad ogni ◮ passo su un insieme Ij già formato. Si considerano tutti i simboli della grammatica ◮ immediatamente alla destra del punto in item di Ij . Per ogni simbolo così individuato, si forma un gruppo ◮ Ik che contiene, inizialmente, gli item ottenuti spostando il punto alla destra del simbolo considerato.
  • 64. LFC Come costruire gli insiemi LR(0) (continua) Parsing di linguaggi liberi Ad esempio, fra gli item di I0 (per la grammatica ◮ Introduzione Parsing top-down appena considerata) ci sono due soli simboli alla Parsing bottom-up destra del punto, S e ( . Per ognuno di essi si creano due nuovi insiemi, I1 e I2 , che contengono inizialmente un solo item ciascuno, S ′ → S· in I1 e S → (·S)S in I2 . Tornando alla descrizione generale, se il nuovo ◮ insieme Ik appena inizializzato contiene item in cui il punto precede un simbolo non terminale A , si aggiungono ad Ik tutti gli item ottenuti dalle produzioni di A inserendo il punto all’inizio. Quest’ultima operazione, che abbiamo già visto più ◮ volte, è detta chiusura dell’insieme Ik .
  • 65. LFC Come costruire gli insiemi LR(0) (continua) Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Ad esempio, ad I2 l’operazione di chiusura aggiunge ◮ gli item S → ·(S)S e S → ·. Ad I1 invece non viene aggiunto nulla, perché l’unico ◮ item iniziale (S ′ → S·) non ha simboli non terminali immediatamente a destra del punto. Se Ik non coincide con nessuno degli insiemi già ◮ generati viene tenuto, altrimenti viene scartato. Il procedimento si arresta quando non ci sono più ◮ insiemi di item da considerare.
  • 66. LFC Esempio Consideriamo la seguente grammatica (aumentata) ◮ Parsing di linguaggi liberi che genera il linguaggio {an bn |n ≥ 1}: Introduzione Parsing top-down Parsing bottom-up ′ →S S S → aSb | ab L’insieme iniziale di item è: ◮ I0 = {S ′ → ·S, S → ·aSb, S → ·ab}. ◮ I simboli immediatamente a destra del punto in I0 ◮ sono S e a, per cui inizializziamo due insiemi: I1 = {S ′ → S·}; ◮ I2 = {S → a · Sb, S → a · b}. ◮ La chiusura non aggiunge nulla a I1 (poiché l’item ◮ S ′ → ·S ha un non terminale immediatamente a destra del punto), mentre aggiunge item ad I2 , che diventa I2 = {S → a · Sb, S → a · b, S → ·aSb, S → ·ab}. ◮
  • 67. LFC Esempio (continua) Parsing di linguaggi liberi Introduzione L’insieme non dà origine ad altri insiemi di item ◮ Parsing top-down Parsing bottom-up (perché non ci sono simboli a destra del punto). Nel’insieme I2 ci sono tre simboli distinti a destra del ◮ punto, per cui formiamo tre insiemi: I3 = {S → aS · b}; ◮ I4 = {S → ab·}; ◮ I5 = {S → a · Sb, S → a · b}. ◮ La chiusura modifica solo I5 , che diviene ◮ I5 = {S → a · Sb, S → a · b, S → ·aSb, S → ·ab}, ◮ ma che viene scartato in quanto coincidente con I2 . Infine lavorando su I3 si ottiene: ◮ I5 = {S → aSb·}, ◮
  • 68. LFC Esempio (continua) Parsing di linguaggi liberi Ricapitolando, gli insiemi LR(0) di item associati alla ◮ Introduzione Parsing top-down grammatica sono: Parsing bottom-up I0 : S ′ → ·S I3 : S → aS · b S → ·aSb S → ·ab I4 : S → ab· I1 : S ′ → S· I5 : S → aSb· → a · Sb I2 : S →a·b S → ·aSb S → ·ab S
  • 69. LFC Funzioni CLOSURE e GOTO Parsing di linguaggi liberi Il prodedimento appena descritto (in maniera ◮ Introduzione Parsing top-down alquanto discorsiva) può essere sinteticamente Parsing bottom-up ricapitolato facendo uso delle due funzioni CLOSURE e GOTO, che lavorano su insiemi di item. Dato un insieme di item I, CLOSURE (I) coincide con ◮ l’insieme I cui siano stati aggiunti tutti gli item del tipo B → ·β (dove B → β è una produzione della grammatica) qualora almeno un item di X sia del tipo A → α · Bγ. Se I è un insieme di item e X un simbolo della ◮ grammatica GOTO(I, X ) è definito come la chiusura dell’insieme J di item del tipo A → αX · β, dove A → α · X β è in I.
  • 70. LFC Parsing di linguaggi liberi Introduzione Parsing top-down Parsing bottom-up Algorithm 2 Insiemi di item LR(0) 1: C ← {CLOSURE ({S ′ → S})} 2: repeat for each I ∈ C do 3: for each X ∈ T ∪ N do 4: if GOTO(I, X ) = {}and GOTO(I, X ) ∈ C then 5: C ← C ∪ {GOTO(I, X )} 6: 7: until No new state is added to C
  • 71. LFC Esempio Parsing di linguaggi liberi Introduzione Da ultimo, consideriamo la costruzione degli insiemi ◮ Parsing top-down Parsing bottom-up di item LR(0) per la grammatica aumentata E′ → E → E +T | T E → T ×F | F T → (E ) | id F che, ricordiamo, non è adatta al parsing top-down. Nella slide seguente presentiamo direttamente la ◮ collezione degli insiemi di item ottenuta applicando l’algoritmo 2.
  • 72. LFC Esempio (continua) Parsing di linguaggi liberi I0 : E ′ → ·E → (·E ) I7 : T → T × ·F I4 : F Introduzione Parsing top-down E → ·E + T → ·E + T F → · (E ) E Parsing bottom-up E → ·T → ·T F → ·id E T → ·T × F → ·T × F T T → ·F → ·F I8 : E → E · +T T F → · (E ) → · (E ) F → (E ·) F F → ·id → ·id F I9 : E → E + T · I1 : E ′ → E · I5 : F → id· T → T · ×F E → E · +T → E + ·T I10 : T → T × F · I6 : E I2 : E → T · → ·T × F T T → T · ×F → ·F I11 : F → (E ) · T → · (E ) F I3 : T → F · → ·id F
  • 73. LFC Automa LR(0) Parsing di linguaggi liberi Come già anticipato, le collezioni di item LR(0) ◮ Introduzione Parsing top-down determinate con la procedura appena descritta Parsing bottom-up costituiscono gli stati dell’automa LR(0) (che, a sua volta, è alla base del parsing SLR(1) che stiamo costruendo). Per completare la descrizione dell’automa è ◮ necessario definire la funzione δ di transizione. In realtà abbiamo già descritto tale funzione, che ◮ coincide “essenzialmente” con la funzione GOTO. Si noti che, tuttavia, che GOTO(I, X ) “costruisce” ◮ nuovi stati e dunque J = GOTO(I, X ) non viene aggiunto se risulta già definito, In tale caso vale comunqe δ(I, X ) = J. ◮
  • 74. LFC Esempio Parsing di L’automa LR(0) per la grammatica linguaggi liberi ◮ Introduzione Parsing top-down Parsing bottom-up ′ →S S S → (S)S | ǫ è: accept I1 $ S S I0 I4 I5 ( ( I2 ) S ( I3
  • 75. LFC Esempio Parsing di L’automa LR(0) per la grammatica linguaggi liberi ◮ Introduzione Parsing top-down Parsing bottom-up ′ →S S S → aSb | ab è: accept I1 $ S b I0 I3 I5 S a b I2 I4 a
  • 76. LFC Esempio L’ultimo esempio è per la grammatica ◮ ′ Parsing di → E E linguaggi liberi → E +T | T E Introduzione → T ×F | F T Parsing top-down Parsing bottom-up → (E) | id F accept $ + T I1 I6 F + E I3 I9 id F ( ) id I0 I8 I11 I5 F id × ( id T F I4 T ( ( F I10 I2 I7 ×
  • 77. LFC Tabelle di parsing SLR(1) Parsing di linguaggi liberi Introduzione Completiamo ora la descrizione del parser con Parsing top-down ◮ Parsing bottom-up l’algoritmo di definizione della tabella di parsing. Le tabelle incorporano le informazioni contenute ◮ nell’automa, che da sono non è sufficiente per eseguire l’analisi (si ricordi che un automa a stati finiti non è in grado di riconoscere linguaggi liberi (che non siano anche regolari). L’algoritmo esamina gli stati dell’automa e le ◮ transizioni uscendi da ciascuno stato. Esso necessita anche di conoscere, per ogni simbolo ◮ non terminale A, l’insieme di simboli FOLLOW (A).
  • 78. LFC Tabelle di parsing SLR(1) (continua) Per ogni stato Ij , consideriamo le transizioni uscenti. ◮ Parsing di linguaggi liberi Se esiste una transizione da Ij a Ik etichettata X ∈ T ◮ Introduzione poniamo ACTION [j, X ] = shift k. Parsing top-down Parsing bottom-up Se esiste una transizione etichettata X ∈ N poniamo ◮ GOTO [j, X ] = shift X . Se nell’insieme di item corrispondenti a Ij esiste un ◮ item A → α·, allora poniamo ACTION [j, X ] = reduce A → α per tutti i simboli X in FOLLOW (A). Se Ij contiene l’item S ′ → S· si pone ◮ ACTION[i, $] = accept. Se, ad un qualunque passo dell’algoritmo, si ◮ manifesta un cosiddetto conflitto shft-reduce (cioè si tenta di inserire in una entry della parte ACTION sia un’azione di shift che una di riduzione) allora la grammatica non è SLR(1).
  • 79. LFC Esempio Parsing di linguaggi liberi Introduzione Per la grammatica ◮ Parsing top-down Parsing bottom-up S → aSb Produzione 1 S → ab Produzione 2 l’algoritmo appena delineato produce la seguente tabella: ACTION GOTO Stato $ S a b 0 shift 2 1 1 accept 2 shift 2 shift 4 3 3 shift 5 4 reduce 2 reduce 2 5 reduce 1 reduce 1
  • 80. LFC Esempio (continua) Parsing di linguaggi liberi Introduzione Parsing top-down Consideriamo il comportamento del parser su input ◮ Parsing bottom-up aabb Stack Input Azione $0 shift 2 aabb$ $02 shift 2 abb$ $022 shift 4 bb$ reduce S → ab $0224 b$ $023 shift 5 b$ reduce S → aSb $0235 $ $01 $ accept
  • 81. LFC Esempio Diamo infine la tabella di parsing per la grammatica ◮ Parsing di E → E + T Prod. 1 T→F Prod. 4 linguaggi liberi Introduzione E→T F → (E ) Prod. 5 Prod. 2 Parsing top-down Parsing bottom-up T → T × F Prod. 3 F → id Prod. 6 ACTION GOTO Stato × + ( ) id $ E T F 0 s5 s4 1 2 3 1 s6 accept 2 r2 s7 r2 r2 3 r4 r4 r4 r4 4 s5 s4 8 2 3 5 r6 r6 r6 r6 6 s5 s4 9 3 7 s5 s4 10 8 s6 s 11 9 r1 s7 r1 r1 10 r3 r3 r3 r3 11 r5 r5 r5 r5
  • 82. LFC Esempio (continua) Consideriamo il comportamento del parser su input ◮ Parsing di id × (id + id) linguaggi liberi Introduzione Parsing top-down Stack Input Azione Parsing bottom-up $0 id × (id + id) $ shift 5 $0 5 × (id + id) $ reduce F → id $0 3 × (id + id) $ reduce T →F $0 2 × (id + id) $ shift 7 $0 2 7 (id + id) $ shift 4 $0 2 7 4 id + id) $ shift 5 $0 2 7 4 5 +id) $ reduce F → id $0 2 7 4 3 +id) $ reduce T →F $0 2 7 4 2 +id) $ reduce E →T $0 2 7 4 8 +id) $ shift 6 $0 2 7 4 8 6 id) $ shift 5 $0 2 7 4 8 6 5 )$ reduce F → id $0 2 7 4 8 6 3 )$ reduce T →F $0 2 7 4 8 6 9 )$ →E +T reduce E $0 2 7 4 8 )$ shift 11 $0 2 7 4 8 11 $ → (E) reduce F $0 2 7 10 $ reduce T →T ×F $0 2 $ reduce E →T $0 1 $ accept