Sunteți pe pagina 1din 8

1

4.5. Analiza sintactica ascendenta. Prezentare generala


Se va prezenta un tip general de analizor numit cu deplasare si reducere. Acesta incearca sa construiasca
arborele sintactic pentru sirul de intrare dat incepind de la frunze spre radacina (ascendent). Procesul poate fi privit de
asemenea si ca reudcerea unui sir de terminale la simbolul de start al gramaticii. In fiecare pas al reducerii se cauta in
forma propozitionala curenta localizarea unui subsir care sa corespunda partii drepte a unei productii. Acest subsir se
inlocuieste cu partea stanga a productiei respective. Daca subsirul este corect ales, la fiecare pas se va parcurge in sens
invers o derivare dreapta.
Exemplu : fie gramatica:
S ->aABe
A->Abc|b
B->d
Sir de intrare abbcde -> aAbcde -> aAde -> aABe -> S
Reducerile corespund parcurgerii in sens invers a urmatoarei derivari drepte:
S -> aABe -> aAde -> aAbcde -> abbcde
4.5.1. Capete
Un capat al unui sir este un subsir care corespunde partii drepte a unei productii si a carui inlocuire cu
neterminalele din partea stanga a productiei reprezinta un pas in parcurgerea in sens invers a unei derivari dreapta. Nu
toate subsirurile care sunt parti drepte ale unei productii A-> si care pot fi localizate ca subsiruri ale unei forme
propozitionale sunt in acelasi timp si capete pentru ca este posibil ca prin reducerea lui la A procesul de reducere sa se
blocheze fara sa se poata face reducerea la simbolul de start al gramaticii.
Exemplu: daca se aplica reducerea aAbcde -> aAAcde procesul de reconstructie se va

bloca

Rezulta ca trebuie data o definitie mai riguroasa notiunii de capat:


Fiind data o forma propozitionala , un capat al sau consta dintr-o productie
A-> si dintr-o propozitie in in care poate fi localizat sirul astfel incat prin
inlocuirea
cu A se
obtine forma propozitionala anterioara a unei derivari dreapta a lui .
Exemplu: S=*> A => ; capatul este format din productia A-> si din propozitia care urmeaza lui .
Observatie: sirul , situat in dreapta capatului contine numai terminale.
In cazul in care gramatica este ambigua, sirul s-ar putea obtine prin mai multe derivari dreapta. Rezulta ca el
ar putea sa contina mai multe capete (deci ambiguitatea e o problema). Daca gramatica nu este ambigua, capatul
corespunzator unei anumite forme propozitionale este unic. Grafic, procesul de reducere al unui capat se poate
reprezenta astfel:
S
a

Capatul reprezinta cel mai din stanga subarbore complet, format dintr-un nod si toti fiii sai. Parintele (in cazul
nostru A) este nodul cel mai de jos si cel mai din stanga avand toti fiii prezenti in arbore sub forma de frunze. Reducerea
unui capat la partea stanga a productiei respective se numeste fasonarea capatului si consta din indepartarea fiilor lui
A din arborele sintactic.
Exemplu: fie gramatica ambigua
E -> E + E
E -> E * E
E -> (E)
E -> id
iar sirul de cautare : id+ id * id
(1) E =d> E + E =d> E + E * E =d> E + E * id3 =d> E + id2 * id3 =d> id1 + id2 * id3

Se observa din Exemplu ca sirurile situate in dreapta unui capat contin exclusiv simboluri terminale. Gramatica
data fiind ambigua exista si urmatoara derivare dreapta distincta de prima:
(2) E =d> E * E =d> E * id3 =d> E + E * id3 =d> E + id2 * id3 =d> id1 + id2 * id3
In forma propozitionala E + E* id3 exista 2 capete: id3, E+E, corespunzatoare primei si respectiv celei de-a
doua derivari dreapta prezentate. La fel ca si in prezentarea acestui Exemplu la discutarea gramaticii ambigue, existenta
celor 2 dericari dreapta semnifica prioritati relative diferite ale operatorilor * si +. La primul model de derivare este
mai prioritar *.
4.5.2. Fasonarea capetelor
Fasonarea capetelor se poate considera si ca procesul de parcurgere in sens invers a unei derivari dreapta. Se
porneste de la un sir de analizat. Daca apartine limbajului descris de gramatica data, atunci el se poate obtine dintr-un
pas oarecare n al unei derivari dreapta inca necunsocute, dar care are urmatoarea forma:
S =d> 0 =d> 1 =d> =d> n =d>
Reconstituirea in sens invers a acestei derivari consta in localizarea in forma propozitionala n a unui capat n si
inlocuirea lui cu partea stanga a productiei. An -> n, obtinandu-se astfel forma propozitionala n-1. Apoi se continua
procedeul cu n-1 etc. Daca in final s-a ajuns la simbolul de start al gramaticii inseamna ca procesul de analiza s-a incheiat
cu succes.
Exemplu: se considera gramatica din Exemplul anterior
E -> E + E | E -> E * E | E -> (E) | E -> id
avand sirul din intrare: id1 + id2 * id3
Secventa de reduceri care apare in continuare reprezinta inversul secventei din prima derivare (unde * este mai
prioritara)
forma propozitionala dreapta
id1 + id2 * id3
E + id2 * id3
E + E * id3
E+E*E
E+E

capat
id1
id2
id3
E*E
E+E

productie pentru reducere


E -> id
E -> id
E -> id
E -> E * E
E -> E + E

4.5.3. Implementarea cu stiva a analizei sintactice de tip deplasare-reducere


Pentru implementarea analizei sintactice bazata pe fasonarea capetelor trebuie rezolvate doua probleme:

1. localizarea subsirului care urmeaza sa fie redus in forma propozitionala curenta


2. in cazul in care gramatica are mai multe productii cu aceeasi parte dreapta, trebuie aleasa productia ce urmeaza a fi
aplicata
Ca structura de date de baza se poate utiliza o stiva in care se vor deplasa simbolurile gramaticale din tamponul
de intrare si se vor localiza capetele. Tamponul de intrare contine sirul de analizat . Baza stivei si respectiv extremitatea
dreapta a sirului de intrare vor fi marcate printr-un simbol special $. La inceput stiva este goala, contine doar simbolul
$ iar la intarre sirul este $. La fiecare pas al analizei, analizorul deplaseaza in stiva 0 sau mai multe simboluri de
intrare, pana in momentul in care in varful stivei apare un capat , apoi se reduce la partea stanga a productiei respective
si se continua ciclic aceste operatii fie pana cand stiva contine doar simbolul de start si intrarea este goala, fie pana cand
se detecteaza o eroare.
In configuratia finala, stiva va contine $S iar in intrare vom avea simbolul $.
Daca se ajunge in aceasta configuratie fara eroare, se va semnala terminarea cu succes a analizei.
Exemplu:
stiva
$
$ id1

intrare
id1 + id2 * id3 $
+ id2 * id3 $

actiune
deplasare
reducere E->id

$E
$E+
$ E + id2
$E+E
$E+E*
$ E + E * id3
$E+E*E
$E+E
$E

1.
2.
3.
4.

+ id2 * id3 $
id2 * id3 $
* id3 $
* id3 $
id3 $
$
$
$
$

deplasare
deplasare
reducere E -> id
deplasare
deplasare
reducere E -> id
reducere E -> E * E
reducere E -> E + E
succes

Din Exemplul anterior rezulta ca analizorul executa urmatoarele 4 actiuni


deplasare : simbolul de intrare curent este introdus in stiva
reducere : se realizeaza in situatia in care extremitatea dreapta a unui capat se afla in varful stivei; analizorul trebuie
sa localizeze extremitatea sa stanga, sa stabileasca neterminalul cu care trebuie inlocuit capatul si sa realizeze
efectiv reducerea lui
acceptare : analizorul semnaleaza terminarea cu succes a analizei
eroare: se apeleaza o rutina de eroare daca s-a descoperit un caz de eroare
Observatie: utilizarea stivei ajuta la localizarea capatului pentru ca el va fi situat intotdeauna in varful stivei si nu in
interior.
Modalitatea concreta a alegerii uneia dintre actiuni depinde de tipul concret de analiza:
- precedenta operatorilor
- left-right

4.5.4. Prefixe viabile


Multimea prefixelor formelor propozitionale dreapta care pot sa apara in stiva unui analizor sintactic cu
deplasare si reducere se numesc prefixe viabile. Altfel spus un prefix viabil este un prefix al unei forme propozitionale
dreapta care nu continua dincolo de extremitatea dreapta a celui mai din dreapta capat al acelei forme propozitionale.
Consecinta acestei definitii este aceea ca intotdeauna este posibil sa se adauge simboluri terminale la extremittea unui
prefix viabil pentru a obtine o forma propozitonala dreapta. Pe parcursul analizei sintactice nu va apare nici o eroare
atata timp cat portiunea de intrare vazuta pana la un anumit punct poate fi redusa la un prefix viabil.
4.5.5. Conflicte in timpul analizei sintactice cu deplasare si reducere
Exista gramatici independente de context la care nu se poate aplica aceasta metoda. Pentru o astfel de gramatica
analizorul poate ajunge intr-o configuratie in care cunoscand intreg continutul stivei si urmatoarele simboluri din
intrare, nu se poate decide daca trebuie efectuata o deplasare sau o reducere. Acest tip de conflict se numeste conflict
deplasare-reducere. In alte situatii analizorul nu poate decide ce reducere sa efectueze intre mai multe posibile. Este
cazul conflictelor reducere-reducere.
Observatie: clasa gramaticilor pentru care pot sa apara astfel de conflicte nu se incadreaza in clasa
LR. Gramatica instructiunii if care este ambigua nu este LR.
<instructiune> -> if <expre> then <instr> |
if <expr> then <instr> altceva
stiva
if <expr> then <instr>

intrare
else $

Pentru configuratia data nu se poate decide daca varful stivei este un capat sau nu. Avem deci un conflict
deplasare-reducere, intrucat in functie de ce urmeaza in intare, poate fi corect sa se reduca if <expr> then <instr> la
<instr> sau ar putea fi corect sa se deplaseze else din intrare in stiva , urmat de cautarea unei alte <instr>, pentru ramura
de else.

Deoarece nu se poate lua decizia pe baza unui singur simbol de anticipare, se spune ca gramatica nu este LR1.
In general nici o gramatica ambigua (ca cea de sus) nu poate sa fie LRk, oricare ar fi k N*. Analizorul sintactic cu
deplasare-reducere poate fi usor adoptat pentru a analiza si gramatici ambigue ca cea de mai sus prin decizia ca orice
conflict de tipul deplasare-reducere sa se rezolve in favoarea deplasarii. Se observa ca luand aceasta decizie in situatia
de mai sus, analiza decurge corect. O alta situatie pentru gramaticile non LR este atunci cand se localizeaza cu
certitudine un capat, dar continutul stivei si simbolul curent din intrare nu sunt suficiente pentru a determina ce productie
trebuie utilizata pentru reducere (conflict reducere-reducere). Exemplu: dispunem de un analizor lexical care furnizeaza
atomul lexical id pentru orice identificator, indiferent de utilizarea acestuia (cazul normal). Presupunem ca in limbajul
ales apelurile de proceduri pe de o parte si refeirea elementelor de tablou pe de alta parte, au aceeasi forma sintactica,
adica un nume si argumentele intre paranteze. Deoarece din punct de vedere semantic traducerea indicilor in referinte de
tablou difera substantial de traducerea argumentelor la apelurile de proceduri si functii, trebuie sa utilizam productii
diferite pentru a genera listele de indici si respectiv listele de argumente, ca in gramatica urmatoare:
(1)
<instr> -> id (<lista_param>)
(2)
<instr> -> <expresie> :=<expresie>
(3)
<lista_param> -> <lista_param>,<parametru>
(4)
<lista_param> -> <parametru>
(5)
<parametru> -> id
(6)
<expresie> -> id (<lista_expr>)
(7)
<expresie> -> id
(8)
<lista_expr> -> <lista_expr>,<expresie>
(9)
<lista_expr> -> <expresie>
Consideram o instructiune care incepe cu a(i,j) si presupunem ca am ajuns in situatia in care primii trei atomi
au fost transferati in stiva.
stiva
intrare
( id
, id ) $
Este evident ca id din varful stivei trebuie redus, dar nu se stie care dintre productii trebuie utilizate. Daca a
este procedura, ar trebui aleasa pentru reducere productia (5), iar daca a este element de tablou, trebuie aplicata
productia (7). Pentru a lua decizia, ar trebui consultata tabela de simboluri (daca acolo informatiile sunt completate). O
solutie pur sintactica la aceasta situatie consta in modificarea analizorului lexical astfel incat sa furnizeze un atom
distinct (procid) cand a este un nume de procedura.
Cu modificarea propusa, pentru cazul cand in situatia de mai sus este un apel de procedura, continutul
stivei si al intrarii este urmatorul:
stiva
intrare
procid ( id
, id ) $
Este clar ca reducerea lui id se face prin (5).
Observatie:
- decizia privind reducerea este luata pe baza simbolului al treilea de sub varful stivei (procid) care nici
macar nu participa la reducere
- rezolvarea conflictelor reducere-reducere pe baza modificarilor de gramatica este metoda generala de
rezolvare a acestei situatii
(1)
<instr> -> procid ( <lista_param>)
4.6. Analiza sintactica bazata pe precedenta operatorilor
Se poate aplica doar la o clasa redusa de gramatici dar este imortanta datorita raspandirii ei. Are avantajul ca
analizorul se poate construi usor manual. Printre alte cerinte esentiale, gramatica la care se aplica acest tip de analiza,
trebuie sa aiba urmatoarele doua proprietati:
1. sa nu aiba productii vide
2. in nici o productie sa nu existe parte dreapta continand doua neterminale adiacente
Gramaticile care respecta conditia 2 se numesc gramatici de operatori

E -> EAE | (E) | -E | id


A -> + | - | * | / | ^
daca se substituie A atunci se obtine
E -> E + E | E E | E * E | E / E | E ^ E | (E) | -E | id
Acest mod de transformare este general si se va urmari cu precadere. O gramatica de operatori nu trebuie
neaparat sa fie o gramatica de expresii.
Metoda are si importante dezevantaje
* sunt dificil de prelucrat atomi care au 2 precedente diferite (de ex. semnul -)
* relatia intre analizor is gramatica nu este intotdeauna biunivoca; este posibil ca analizorul sa accepte
siruri (ca si corecte) care nu fac parte din limbajul gramaticii (nu se semnaleaza toate erorile)
* clasa de gramatici acceptate este redusa
Datorita simplitatii ei, acesata tehnica s-a utilizat in multe compilatoare existente pentru analiza expresiilor in
timp ce instructiunile limbajului si celelalte constructii de nivel inalt sunt analizate prin alte metode (de Exemplu cu
descendenti recursivi). Exista totusi si compilatoare pentru limbaje intregi bazate pe aceasta tehnica.
4.6.1. Definirea relatiei de precedenta
In analiza sintactica bazata pe precedenta operatorilor se definesc 3 relatii de precedenta disjuncte: <, =, > care
se stabilesc intre anumite perechi de terminale ale gramaticii. Pe baza acestor relatii se selecteaza capatul formei
propozitionale in stiva analizorului. Semnificatia lor este urmatoarea:
relatia
a<b
a=b
a>b

semnificatia
a cedeaza precedenta lui b
a are aceeasi precedenta cu b
a are precedenta fata de b

Aceste relatii se deosebesc in esenta fata de relatiile similare din algebra:


* au cu totul alta semantica decat precedenta algebrica
* este posibil ca intre 2 terminale sa nu existe nici o relatie de precedenta
* este posibil ca intre 2 terminale sa existe 2 relatii de precedenta
Stabilirea relatiei de precedenta intre terminale se poate face pe 2 cai:
a. intuitiv, pornind de la semnificatia algebrica a operatorilor si tinand cont de asociativitatea lor
* > +, + < *
prin introducerea acestor doua relatii se rezolva si ambiguitatea gramaticii expresiilor, fara sa mai fie
necesara transformarea ei
b. in mod automat, aplicand un algoritm adecvat pentru aceasta; pentru aceasta varianta trebuie eliminata in
prealabil ambiguitatea din gramatica astfel incat ea sa reflecte corect asociativitatea si precedenta
operatorilor
4.6.2. Utilizarea relatiilor de precedenta a operatorilor
Scopul introducerii relatiilor de precedenta este acela de a determina capatul in forma propozitionala dreapta
curenta , astfel simbolul < marcheaza extremitatea stanga a capatului, = interiorul unui capat, iar > marcheaza
extremitatea dreapta a capatului.
Pentru detalii consideram o forma propozitionala dreapta a unei gramatici de operatori. Deoarece partile drepte
ale productiilor nu contin doua neterminale adiacente, rezulta ca nici in forma propozitionala dreapta nu pot apare 2
neterminale adiacente. La modul general, o forma propozitionala dreapta de acest fel poate fi notata astfel: 0 a1 1 an n
unde fiecare i este fie fie un singur neterminal si fiecare ai este un singur terminal. Intr-o astfel de forma propozitionala
dreapta se cauta relatia de precedenta intre perechi de terminale vecine, presupunandu-se ca intre ele exista o unica
relatie de precedenta. Extremitatile sirului de analizat, se marcheaza cu $ si se introduc relatiile de precedenta: $ < a,
a>$. In continuare, se elimina neterminalele din sir iar in sirul ramas (continand numai terminale si $) se introduc
relatiile de precedenta corecte, <,=,>. Capatul se determina prin urmatorul procedeu:
1. se baleiaza sirul cu relatia de precedenta introduse din extremitatea sa stanga pana la intalnirea primului >; acesta va
reprezenta marginea dreapta a capatului,

2. se baleiaza sirul inapoi omitand eventualele simboluri = pana se intalneste primul marcaj <, acesta va fi marginea
3.
4.

stanga a capatului.
se considera ca fiind capat sirul de simboluri gramaticale dintre cele 2 marcaje, inclusiv neterminalele care apar ca
incluse sau inconjurand terminalele dintre marcaje. Considerarea neterminalelor inconjuratoare este necesara pentru
a asigura ca nu vor apare doua neterminale adiacente in forma propozitionala urmatoare.
Exemplu: se considera sirul de intrare $ id + id * id $ si matricea de relatii de precedenta redusa de mai jos:
id

id
+
*
$

+
*
$
>
>
>
<
>
<
>
<
>
>
>
<
<
<
In tabela, operatorul de adunare este asociativ la stanga, in timp ce operatorul de inmultire asociativ la dreapta
Sirul initial, avand introduse relatiile de precedenta este urmatorul:
$ < d > + < id > * < id > $
capat
$ E + id * id $
$ < + < id > * < id > $
$ E + E * id $
$ < + < * < id > $

$E+E*E$
$<+<*>$
E -> E * E
$E+E$
$<+>$
$ E $ => sfarsit

Observatie: deoarece neterminalele nu influenteaza analiza, nu trebuie facuta distinctie intre ele.
In stiva analizorului este suficient sa se tina un singur marcaj reprezentativ pentru orice fel de terminal pentru a
marca doar locul din stiva corespunzator acelui neterminal, util pentru inregistrarea atributelor semantice
corespunzatoare neterminalului respectiv. Aparent din Exemplul de mai sus s-ar putea deduce ca pentru determinarea
capatului ar fi necesara baleierea intregii forme propozitionale sau in orice caz a unei mari portiuni din ea. Acest lucru
nu este necesar intrucat implementarea concreta se face tot pe baza mecanismului de analiza sintactica cu deplasare si
reducere iar relatiile de precedenta sunt utile doar pentru a dirija actiunile analizorului (deplasare sau reducere).
Actiunile analizorului sunt cele cunoscute :
1. deplasare : cat timp nu s-a gasit extremitatea dreapta a capatului, adica intre simboulu terminal cel mai apropiat de
varful stivei si simbolul curent din intrare este valabila una din realatiile < sau =.
2. reducere : cand s-a gasit extremitatea dreapta a capatului adica intre terminalul cel mai propiat din varful stivei si
simbolul de intrare este relatia >, se baleiaza in sens invers stiva pana la intalnirea primului marcaj < dupa care se
face reducere.
3. acceptare : cand ambele simboluri care se compara ( adica varful stivei si simbolul curent din intrare) sunt $
4. eroare : daca se compara 2 simboluri intre care nu exista relatie de precedent
Aceste idei privind mecanismul de analiza sintactica bazata pe precedenta operatorilor sunt cuprinse in urmatorul
algoritm.
Algoritmul primeste la intrare sirul w de analizat si matricea relatiilor de precedenta. Daca w este corect, se va
obtine la iesire un schelet al arborelui sau sintactic, in caz contrar un mesaj de eroare, datorita substituirii
intermediare a neterminalului, motiv pentru care nodurile interioare vor fi uniforme.
stiva
intrare
$
w$
(1) * se pozitioneaza pointerul de intrare pe primul simbol din w
(2) repeat forever
if *atat simbolul din varful stivei cat si simb. curent de intrare sunt $ then return
else
begin
*fie a simbolul terminale cel mai apropiat de varful stivei,
b simbolul curent din intrare
if (a < b) or ( a=b) then

begin {deplasare}
* introdu b in stiva
* avanseaza cu o pozitie pointerul de intrare
end
else if (a >b) then {reducere}
repeat
* extrage din stiva
until *terminalul din varful stivei este in relatie < cu terminalul cel mai recent extras
else *eroare
end;
4.6.3. Deducerea intuitiva a relatiei de precedenta din asociativitatea si prioritatea algebrica a operatorilor
Singura cerinta care trebuie avuta in vedere este aceea ca ele sa conduca la analiza corecta a limbajului defninit
de gramatica. Tinand cont de faptul ca analiza sintactica bazata pe precedenta operatorilor se aplica la gramatici pentru
expresii sau similare cu acestea iar intre operatorii din expresii exista reguli de priorotate si asociativitate riguroase care
rezolva eventualele ambiguitati, aceste reguli pot reprezenta baza pentru stabilirea relatiei de precedenta. Pentru cazul
operatorilor binari, notati cu , 1, 2 relatiile de precedenta pot fi deduse astfel:
1. daca 1 este mai prioritar algebric decat 2, se introduc in tabela relatiile
1 > 2 si 2 < 1.
2. daca 1 si 2 au prioritate algebrica egala (inclusiv cand reprezinta acelasi operator) apar urmatoarele 2 situatii:
a. 1 si 2 sunt asociativi la stanga
1 > 2 si 2 > 1
b. 1 si 2 sunt asociativi la dreapta
1 < 2 si 2 < 1
3. pentru orice operator exista urmatoarele relatii de precedenta cu celelalte terminale
id
< id
id >
<(
(<

( si )
>)
)>
>$
$>$

Pentru a asigura reducerea la E a lui id si a lui (E), intre terminalele care nu sunt operatori se introduc
urmatoarele relatii:
(=)
(<(
( < id

$<(
id > $
id > )

$ id
)<$
)>)

Exemplu:
Se considera gramatica:
E -> E + E | E E | E * E | E / E | E ^ E | ( E ) | id
Operatorii au urmatoarele proprietati:
^
*,/
+.-

cel mai prioritar, asociativ la stanga


asociativi la stanga
cea mai mica prioritate, asociativi la stanga
Pe baza relatiilor de precedenta de mai sus, rezulta urmatoarea matrice de precedente:
+

+
>

>

*
<

/
<

^
<

id
<

(
<

)
>

$
>

*
/
^
id
(
)
$

>
>
>
>
>
<
>
<

>
>
>
>
>
<
>
<

id * ( id ^ id ) id / id
$ < id > * < ( < id > ^ < id > ) > - < id > / < id > $
$ < * < ( < id > ^ < id > / < id > ) > -.
$ < * < ( < ^ < id > ) .
$ < * < ( < ^ > ) > - < id > / < id > $
$ < * < ( = ) > - < id > / < id > $
$ < * > - < .
$ < - < id > / < id > $
$ < - < / < id > $
$<-</>$
$<->$

<
>
>
>
>
<
>
<

<
>
>
>
>
<
>
<

<
<
<
<
>
<
>
<

<
<
<
<

<
<
<
<

<

<

<

<

>
>
>
>
>
=
>

>
>
>
>
>
>