Analizor sintactic cu descendenti
recursivi Analizor sintactic cu operatori precedenti

Proiectarea unui analizor sintactic
descendent nerecursiv LL(1)












Principiul analizei sintactice descendente nerecursive

Este acelasi ca si in cazul analizorului cu descendenti recursivi, in sensul ca procesul de analiza se realizeaza prin derivari succesive ale neterminalelor, incepand cu radacina, dar implementarea este diferita, si anume, derivarile nu se transpun ca secvente de cod, ci se bazeaza pe structuri de date in care se indica ramurile de derivare.

Structurile folosite sunt:

O tabela de analiza, ale carei intrari contin cate o productie a gramaticii (dupa care se face derivarea la un moment dat).
O stiva de analiza in care se introduc simboluri ale gramaticii.
Pe baza simbolului aflat in varful stivei si a simbolului curent din sirul de intrare, analizorul decide care din urmatoarele actiuni se vor executa: Construirea tabelei se poate face automat, pe baza unor algoritmi care vor fi prezentati in aceasta lucrare. Cum algoritmul de analiza este acelasi, indiferent de continutul tabelei, rezulta ca acest tip de analizor sintactic poate fi generat automat, spre deosebire de analizorul cu descendenti recursivi.

Restrictiile care se impun asupra unei gramatici, in vederea aplicarii analizei de tip LL(1) sunt aceleasi ca si in cazul analizei cu descendenti recursivi, si anume:


Algoritmul de analiza

Vom nota:


Algoritmii de construire a tabelei de analiza

Tabela de analiza va avea un numar de linii egal cu numarul neterminalelor si numarul de coloane egal cu numarul terminalelor (inclusiv simbolul $).

Completarea tabelei de analiza
Notand cu G gramatica de analizat si cu TabSIN tabela de analiza, completarea elementelor acesteia se va face dupa urmatoarea procedura:
procedure complTabSIN is
    for fiecare relatie alfa din G  do
        for fiecare a din PRIM(alfa) do
          TabSIN[A, a] = alfa
         endfor
        if  Epsilon este in PRIM(alfa) then
          for fiecare b din URM(A) do
            TabSIN [A, b] = alfa
          endfor
          if Epsilon este in URM(A) then
           TabSIN [A, $] = alfa
          endif
        endif
    endfor
    for fiecare TabSIN [A, a] necompletat  do
        *completeaza TabSIN [A, a] cu apel de eroare
    endfor
end complTabSIN
O multime PRIM(alfa) cuprinde toate terminalele a care satisfac conditia:

alfa a beta

adica  a se afla pe prima pozitie in siruri derivate din alfa.

O multime URM(A) contine toate terminalele a care pot sa apara imediat in dreapta lui A, intr-un sir oarecare al limbajului sursa:

alfa A a beta

Calculul acestor multimi se face conform algoritmilor de mai jos:
 

Calculul multimilor PRIM

procedure calculPRIM(alfa) is
    *initializeaza PRIM(alfa) cu multimea vida
    if alfa este format dintr-un singur simbol X then
        if X este terminal then
            PRIM (X) = { X }
        else
            for toate relatiile a beta do
                    *adauga a la PRIM (X)
            endfor
            if exista Epsilon then
                    *adauga Epsilon la PRIM (X)
           endif
            for toate relatiile Y1Y2 . . .Yn, cu Y1 - neterminal do
                    *adauga PRIM (Y1) - { Epsilon } la PRIM(X)
                   gata = false
                   i = 2
                   repeat
                       if  Epsilon nu este in PRIM(Yi-1) then
                           gata = true
                       else
                            *adauga PRIM (Yi ) - { Epsilon } la PRIM (X)
                           i = i + 1
                           if  i > n then
                                *adauga Epsilon la PRIM(X)
                           endif
                       endif
                   until  i > n or gata
            endfor
        endif
    else
        *consideram ca alfa este de forma X1X2 . . . Xm
        *adauga PRIM (X1)- { Epsilon } la PRIM (alfa )
        i = 2
        gata = false
        repeat
            if Epsilon nu este in PRIM (Xi-1) then
               gata = true
            else
                *adauga PRIM (Xi ) - { Epsilon } la PRIM (alfa)
               i = i + 1
               if  i > m then
                    *adauga Epsilon la PRIM (alfa)
               endif
            endif
        until  i > m or gata
    endif
end  calculPRIM

Calculul multimilor URM
procedure calculURM(A) is
    *initializeaza URM(A) cu multimea vida
    if  A este radacina gramaticii then
        *adauga Epsilon la URM(A)
    endif
    for toate productiile Balfa A beta do
        *adauga PRIM(beta)- { Epsilon } la URM(A)
        if beta este sirul vid or Epsilon este in PRIM(beta) then
            *adauga URM(beta) la URM(A)
        endif
    endfor
end  calculURM(A)


Multimile PRIM si URM corespunzatoare fiecarui neterminal al gramaticii se pot reprezenta folosind cate o tabela, TabPRIM, respectiv TabURM, cu elemente de tip boolean, indexate pe verticala cu neterminale, iar pe orizontala cu terminale; un element de coordonate [A, a] oarecare al acestor tabele va contine valoarea true daca a este in PRIM(A), respectiv a este in URM(A), si false in caz contrar.

Daca, in urma completarii tabelei de analiza, se obtin intrari multiple (care contin doua sau mai multe productii), inseamna ca gramatica respectiva este ambigua. In cazul limbajelor de programare cunoscute, de cele mai multe ori, la o analiza atenta a gramaticii, proiectantul poate stabili care dintre productii poate ramane in tabela, la elementul in cauza.
 
 

Desfasurarea lucrarii

Se va realiza un generator de analizor LL(1) care sa primeasca la intrare productiile unei gramatici date de conducatorul lucrarii (sau cea din Anexa B). Analizorul rezultat va tipari la executie, la fiecare pas, urmatoarele informatii:

Daca se ajunge intr-o situatie de eroare, se va afisa un mesaj corespunzator si se va abandona analiza.
Tratarea productiilor primite la intrare se va realiza utilizand gramatica propusa in Anexa F.

Analizor sintactic cu descendenti
recursivi Analizor sintactic cu operatori precedenti