Generatorul de analizoare lexicale LEX Analizoare sintactice LL(1)

Proiectarea unui analizor sintactic
cu descendenti recursivi





Rolul analizei sintactice

Analizorul sintactic verifica daca un program respecta regulile de sintaxa ale limbajului sursa. Intrarea analizorului sintactic este constituita din sirul codurilor lexicale furnizate de analizorul lexical. In metodele de analiza sintactica descrise in prezentul indrumator se va considera cazul in care analizorul lexical este apelat de cel sintactic ori de cate ori este necesar un nou atom.
Regulile de sintaxa pe care analizorul sintactic trebuie sa le cunoasca sunt descrise fie prin intermediul gramaticilor BNF, fie prin diagrame de sintaxa.
In cazul analizei cu descendenti recursivi diagramele de sintaxa sunt mai sugestive si mai usor de implementat.
 

Principiul analizei cu descendenti recursivi

Este o metoda de analiza descendenta, fara reveniri, aplicabila acelor gramatici care nu prezinta recursivitate la stanga si nici ambiguitati.

Analizorul cu descendenti recursivi este format dintr-o colectie de proceduri care se apeleaza intre ele in mod recursiv. Practic, fiecarui neterminal din gramatica ii corespunde o procedura.

In cazul acestui tip de analizor arborele sintactic nu apare explicit, ca o structura de date, ci poate fi vazut mai degraba ca un arbore al apelurilor de proceduri.

Procesul de analiza consta in derivari succesive ale neterminalelor, incepand cu radacina. Derivarile se materializeaza prin apelarea procedurilor corespunzatoare simbolurilor ce apar pe o anumita ramura de derivare. Selectia ramurii de derivare pentru un neterminal oarecare A, trebuie sa fie unic determinata de simbolul curent din sirul de intrare (altfel metoda cu descendenti recursivi nu se poate aplica) si ea decurge astfel:

Daca a este simbolul curent de intrare, iar pentru A exista mai multe relatii de transformare:
Aalfa1 | alfa2 | . . . | alfan
se alege alternativa alfai care incepe cu a sau conduce prin derivare la un sir care incepe cu a. Cazul in care exista mai multe asemenea alternative se poate rezolva de obicei prin factorizare la stanga.

Daca nu exista nici o alternativa care sa satisfaca conditia:

alfaia beta
dar exista relatia AEpsilon (alternativa vida), se alege aceasta cale de derivare (practic, se considera A ca recunoscut, iar analiza se continua cu simbolul ce urmeaza lui A pe calea de derivare curenta ).

Daca nu exista nici alternativa vida pentru A, atunci se considera ca s-a ajuns intr-o situatie de eroare.


Eliminarea recursivitatii stangi

O gramatica este recursiva la stanga, daca contine cel putin un neterminal A pentru care exista relatia:

AA alfa

Recursivitatea la stanga trebuie eliminata in cazul analizei cu descendenti recursivi, deoarece conduce la apeluri recursive infinite.

Algoritmul de eliminare a recursivitatii stangi este:

procedure elimRecursStanga is
    *fie A1, A2, ... , An - neterminalele gramaticii
    for  i = 2 . . n do
        for  j = 1 . . i - 1 do
            * inlocuieste toate productiile de forma AAj gamma prin
                 Aidelta1 gamma | delta2 gamma | . . . | deltak gamma , unde
                Ajdelta1 | delta2 | . . . | deltak   reprezinta toate productiile corespunzatoare lui Aj;
            *elimina recursivitatea stanga imediata pentru Ai, daca este cazul;
        endfor
    endfor
end elimRecursStanga
Recursivitatea stanga imediata apare atunci cand exista cel putin o relatie de forma:

AA alfa1 | A alfa2 | . . . | A alfam | beta1 | beta2 | . . . | betal

Aceasta se rezolva prin introducerea unui neterminal suplimentar A' si a urmatoarelor relatii:

Abeta1 A' | beta2 A' | . . . | betal A'

A' alfa1 A' | alfa2 A' | . . . | alfal A' | Epsilon


Factorizarea la stanga

Se aplica in cazurile in care avem relatii de forma:

Aalfa beta1 | alfa beta2

care conduc la situatii de ambiguitate, analizorul sintactic neputand decide ce ramura de derivare sa aleaga. Asemenea cazuri se rezolva prin introducerea unui neterminal suplimentar A' si a urmatoarelor relatii:

Aalfa A'
A'beta1 | beta2




Proiectarea analizoarelor cu descendenti recursivi, pe baza diagramelor de sintaxa

Fiecare diagrama se implementeaza printr-o procedura, aplicand urmatoarele reguli:

Daca in diagrama apare o tranzitie spre un neterminal, se va prevedea un apel la rutina corespunzatoare acelui neterminal.

Daca in diagrama apare o tranzitie spre un terminal, se vor prevedea:


Pentru exemplificare vom considera urmatorul set de diagrame:
 


Procedurile corespunza toare sunt:
 
procedure text is
    call instr
    while atom = pctVirg do
        call ALEX
        call instr
    endwhile
end text
procedure instr is
    select  atom of
        cheieWHILE:
           call ALEX
           call instr_while
        end cheieWHILE
        ident:
           call ALEX
           call instr_atrib
        end ident
        else
            *semnaleaza eroare
    endselect
end instr

procedure instr_while is
    call expr
    if tom = cheieDO then
        call ALEX
        call instr
    else
        *semnaleaza eroare
    endif
end instr_while

procedure instr_atrib is
    if atom = opAtrib then
        call ALEX
        call expr
    else
        *semnaleaza eroare
    endif
end instr_atrib
Lansarea in executie a analizorului se face apeland procedura corespunzatoare radacinii gramaticii (in exemplul de mai sus aceasta ar fi text).

La intocmirea procedurilor de analiza se va tine cont de urmatoarea regula:

La intrarea intr-o procedura analizorul trebuie sa fie deja pozitionat pe un nou atom din textul sursa, ceea ce presupune ca inainte de terminarea unei proceduri trebuie apelat analizorul lexical. Acest apel trebuie prevazut si inaintea lansarii in executie a analizorului sintactic.


Desfasurarea lucrarii

Se va construi un analizor cu descendenti recursivi pentru gramatica propusa in Anexa B, sau o alta gramatica data de conducatorul lucrarii. La semnalarea unei erori se va emite un mesaj corespunzator, si se va stopa analiza.
Programul principal utilizat pentru testare va fi:

MainProgram is
    *sectiune de initializari
    call GetCarUrm
    call ALEX
    call *procedura corespunzatoare radacinii gramaticii
    if nu s-a detectat eroare then
        *afiseaza mesaj 'Program corect d.p.d.v. sintactic'
    endif
end MainProgram

Pe durata punerii la punct a procedurilor se recomanda utilizarea unor jaloane (tipariri de mesaje plasate la intrarea in proceduri) prin care sa se realizeze o trasare a drumului parcurs de analizor.
Pentru analiza lexicala se va utiliza unul dintre analizoarele proiectate la lucrarile anterioare.

Generatorul de analizoare LEX Analizoare sintactice LL(1)