This shows you the differences between two versions of the page.
cpl:labs:02 [2015/12/06 19:46] laura.vasilescu [Secțiunea de declarații Bison] |
cpl:labs:02 [2016/10/05 23:18] (current) bogdan.nitulescu [Secțiunea de declarații Bison] |
||
---|---|---|---|
Line 14: | Line 14: | ||
* LALR(1) - Look Ahead Left to Right with a one-token lookahead | * LALR(1) - Look Ahead Left to Right with a one-token lookahead | ||
* GLR - Generalized Left to Right | * GLR - Generalized Left to Right | ||
- | Majoritatea parserelor folosesc LALR(1), care are mai putine capabilitati, insa este semnificativ mai rapid si mai usor de folosit decat GLR. Si noi, vom genera tot parsere LALR. | + | Majoritatea parserelor folosesc LALR(1), care are mai putine capabilitati, insa este semnificativ mai rapid si mai usor de folosit decat GLR. Si noi vom genera tot parsere LALR. |
''Bison'' este varianta GNU a ''yacc'' (Yet Another Compiler-Compiler). | ''Bison'' este varianta GNU a ''yacc'' (Yet Another Compiler-Compiler). | ||
Line 72: | Line 72: | ||
* simbolul de start al gramaticii (opțional) | * simbolul de start al gramaticii (opțional) | ||
<code C> | <code C> | ||
- | %start <tip> nume1 nume2 | + | %start <tip> nume |
</code> | </code> | ||
Line 98: | Line 98: | ||
%type <int_value> exp | %type <int_value> exp | ||
</code> | </code> | ||
- | Dacă este suficient să întroarcem valori de tip int, și nu definim un union de tipuri, atunci ''<token_type>'' este omis în declararea tokenilor. | + | Dacă este suficient să întoarcem valori de tip int, și nu definim un union de tipuri, atunci ''<token_type>'' este omis în declararea tokenilor. |
* Precedența %left. Aceasta va fi detaliată intr-un subcapitol ulterior. | * Precedența %left. Aceasta va fi detaliată intr-un subcapitol ulterior. | ||
- | O listă completă cu simbolii ''bison'' găsiți [[http://www.gnu.org/software/bison/manual/html_node/Table-of-Symbols.html|aici]] | + | O listă completă cu simbolurile ''bison'' găsiți [[http://www.gnu.org/software/bison/manual/html_node/Table-of-Symbols.html|aici]] |
| | ||
==== Secțiunea de reguli gramaticale ==== | ==== Secțiunea de reguli gramaticale ==== | ||
Line 123: | Line 123: | ||
<code C> | <code C> | ||
- | expr : NUM { $$ = $1;} | + | expr |
+ | : NUM { $$ = $1;} | ||
| expr ‘+’ expr { $$ = $1 + $3;} | | expr ‘+’ expr { $$ = $1 + $3;} | ||
; | ; | ||
Line 133: | Line 134: | ||
{C statements} | {C statements} | ||
</code> | </code> | ||
- | <note>Bison nu verifica corectitudinea codului C din acțiuni ci doar îl copiaza în fișierul .c al parserului, unde va fi verificat de compilatorul de C. Așadar erorile de C vor fi raportate abia la compilarea parserului.</note> | + | <note>Bison nu verifică corectitudinea codului C din acțiuni ci doar îl copiaza în fișierul .c al parserului, unde va fi verificat de compilatorul de C. Așadar erorile de C vor fi raportate abia la compilarea parserului.</note> |
Acțiunea poate fi plasată la sfârșitul unei alternative sau chiar în interiorul acesteia. | Acțiunea poate fi plasată la sfârșitul unei alternative sau chiar în interiorul acesteia. | ||
* ''$$'' reprezinta rezultatul, adica valoarea ce va fi atribuită neterminaului de la stânga regulii, cel la care se va reduce regula. | * ''$$'' reprezinta rezultatul, adica valoarea ce va fi atribuită neterminaului de la stânga regulii, cel la care se va reduce regula. | ||
* ''$n'' este al n-lea termen din regula sintactica. | * ''$n'' este al n-lea termen din regula sintactica. | ||
- | Acțiunile definite în mijlorul unei reguli se folosesc numai în anumite situații, pot folosi doar simbolii anteriori acesteia (fiindcă se execută înainte ca simbolii următori regulii să fie parsați) și sunt o sursă de conflicte. Vom reveni asupra acestora în laboratorul urmator. | + | Acțiunile definite în mijlocul unei reguli se folosesc numai în anumite situații, pot folosi doar simboluri anterioare acesteia (fiindcă se execută înainte ca simbolurile următoare regulii să fie parsați) și sunt o sursă de conflicte. Vom reveni asupra acestora în laboratorul următor. |
==== Secțiunea de cod C ==== | ==== Secțiunea de cod C ==== | ||
Secțiunea de cod C trebuie să conțină: | Secțiunea de cod C trebuie să conțină: | ||
Line 145: | Line 146: | ||
</code> | </code> | ||
* definiția funcției ''main'' care conține un apel către ''yyparse()'' | * definiția funcției ''main'' care conține un apel către ''yyparse()'' | ||
- | ===== Flex si bison/yacc ===== | + | ===== Flex și bison/yacc ===== |
- | Vom vedea acum cum comunica analizorul lexical (flex) cu analizorul sintactic (bison). | + | Vom vedea acum cum comunică analizorul lexical (flex) cu analizorul sintactic (bison). |
- | ''Bison'' genereaza un parser, din ''nume_gram.y'', in fisierul ''nume_gram.tab.c'' si un fiser header ''nume_gram.tab.h''. In fisierul header fiecare token are asociat un numar. Fisierul de intrare pentru lex (.l) include fisierul header (cu extensia ''.tab.h'') si foloseste numerele atribuite tokenilor in acest fisier. | + | ''Bison'' generează un parser, din ''nume_gram.y'', în fișierul ''nume_gram.tab.c'' și un fișier header ''nume_gram.tab.h''. În fișierul header fiecare token are asociat un numar. Fișierul de intrare pentru lex (.l) include fișierul header (cu extensia ''.tab.h'') și folosește numerele atribuite tokenilor în acest fisier. |
{{ :cpl:labs:flex_bison.png?600 |}} | {{ :cpl:labs:flex_bison.png?600 |}} | ||
- | Intrarea pentru ''bison'' este un sir de tokeni furnizat de flex prin functia yylex(). | + | Intrarea pentru ''bison'' este un șir de tokeni furnizat de flex prin funcția yylex(). |
- | Numerele associate tokenilor: | + | Numerele asociate tokenilor: |
- | * Numarul unui token literal este valoarea sa ASCII | + | * Numărul unui token literal este valoarea sa ASCII |
<code C> | <code C> | ||
[-+] return *yytext; | [-+] return *yytext; | ||
</code> | </code> | ||
- | va returna valoarea ASCII a caracterului ‘-’ (45), respective valoarea ASCII a caracterului ‘+’ (43) | + | va returna valoarea ASCII a caracterului ‘-’ (45), respectiv valoarea ASCII a caracterului ‘+’ (43) |
- | * Ceilalti tokeni primesc numere incepand cu 257 | + | * Ceilalți tokeni primesc numere începând cu 257 |
<note>Token-ul ''error'' este rezervat pentru tratarea erorilor</note> | <note>Token-ul ''error'' este rezervat pentru tratarea erorilor</note> | ||
- | Sa presupunem ca avem o gramatica care defineste token-ul INTEGER si acesta a primit valoarea 258 in fisierul header nume_gram.tab.h. | + | Să presupunem că avem o gramatică care definește token-ul INTEGER și acesta a primit valoarea 258 în fișierul header nume_gram.tab.h. |
- | Fiecare aparitie a instructiunii “return INTEGER” din fisierul .l va fi inlocuita, in acest caz cu “return 258”. | + | Fiecare apariție a instrucțiunii “return INTEGER” din fișierul .l va fi înlocuită, în acest caz cu “return 258”. |
- | Pentru a obtine tokenii, bison apeleaza functia yylex. Altfel spus, fisierul cu extensia .tab.h atribuie fiecarui token/terminal un identificator unic de tip int. Cum acest header este inclus in fisierul *.l, numele tokenilor sunt inlocuite cu identificatorul unic. | + | Pentru a obține tokenii, bison apelează funcția yylex. Altfel spus, fișierul cu extensia .tab.h atribuie fiecărui token/terminal un identificator unic de tip int. Cum acest header este inclus în fisierul *.l, numele tokenilor sunt înlocuite cu identificatorul unic. |
- | Daca un token are asociata o valoare, aceasta poate fi transmisa catre parser prin variabila ''yylval''. | + | Dacă un token are asociată o valoare, aceasta poate fi transmisă către parser prin variabila ''yylval''. |
- | (in cazul nostru un int) | + | (în cazul nostru un int) |
- | <note>Atentie: Nu faceti confuzie intre id-ul token-ului dat de bison (identificator unic) si valoarea pe care o trimite din yylex. | + | <note>Atenție: Nu faceți confuzie între id-ul token-ului dat de bison (identificator unic) și valoarea pe care o trimite din yylex. |
</note> | </note> | ||
- | ====== Conflicte si ambiguitati ====== | + | ====== Conflicte și ambiguități ====== |
**Conflictele** sunt rezultatul unei gramatici ambigue. Conflictele pot fi fie shift/reduce, fie reduce/reduce | **Conflictele** sunt rezultatul unei gramatici ambigue. Conflictele pot fi fie shift/reduce, fie reduce/reduce | ||
- | * Intr-un conflict shift/reduce actiunea implicita este cea de shift. | + | * Într-un conflict shift/reduce acțiunea implicită este cea de shift. |
- | * Intr-un conflict reduce/reduce actiunea implicita este de a reduce folosind prima regula aplicabila a gramaticii. | + | * Într-un conflict reduce/reduce acțiunea implicită este de a reduce folosind prima regulă aplicabilă a gramaticii. |
- | Exemplu de conflict shift/reduce datorat ambiguitatii ''dangling else'': | + | Exemplu de conflict shift/reduce datorat ambiguității ''dangling else'': |
- | Cand token-ul ''else'' este citit si devine token-ul lookahead, ceea ce a fost deja citit se potriveste pe prima regula si ar putea fi redus. Dar, este de asemenea legal sa shiftam else-ul, pentru ca sirul de tokeni de la intrare s-ar putea potrivi pe a doua regula. | + | Când token-ul ''else'' este citit și devine token-ul lookahead, ceea ce a fost deja citit se potrivește pe prima regulă și ar putea fi redus. Dar, este de asemenea legal să shiftam else-ul, pentru ca șirul de tokeni de la intrare s-ar putea potrivi pe a doua regulă. |
- | Deoarece parser-ul prefera sa shifteze, else-ul va fi atasat if-ului cel mai imbricat. | + | Deoarece parser-ul preferă sa shifteze, else-ul va fi atașat if-ului cel mai imbricat. |
- | Daca parser-ul ar alege sa reduca, atunci cand poate, si nu sa shifteze, else-ul va fi atasat if-ului exterior(primul). | + | Dacă parser-ul ar alege să reducă, atunci când poate, și nu să shifteze, else-ul va fi atașat if-ului exterior(primul). |
<code C> | <code C> | ||
Line 193: | Line 194: | ||
</code> | </code> | ||
- | Cu aceasta gramatica, secventa de intrare ''if (exp1) if (exp2) stmt1 else stmt2'' poate fi parsata in 2 moduri diferite: | + | Cu această gramatică, secvența de intrare ''if (exp1) if (exp2) stmt1 else stmt2'' poate fi parsată în 2 moduri diferite: |
<code C> | <code C> | ||
Cazul 1: if (e1) { if (e2) s1 else s2 } | Cazul 1: if (e1) { if (e2) s1 else s2 } | ||
Line 200: | Line 201: | ||
</code> | </code> | ||
- | Acesta este un asa zis conflict shift/reduce legitim. Exista cazuri in care gramatica genereaza astfel de conflicte pentru a fost scrisa ambiguu, desi se putea scrie si intr-o forma neambigua. In aceste cazuri se recomanda rescriere regulilor cu conflicte. | + | Acesta este un asa zis conflict shift/reduce legitim. Există cazuri în care gramatica generează astfel de conflicte pentru că a fost scrisa ambiguu, deși se putea scrie și într-o formă neambiguă. În aceste cazuri se recomandă rescriere regulilor cu conflicte. |
<code C> | <code C> | ||
- | expr : expr ’+’ expr | + | expr |
+ | : expr ’+’ expr | ||
| expr ’*’ expr | | expr ’*’ expr | ||
| ’-’ expr | | ’-’ expr | ||
Line 209: | Line 211: | ||
; | ; | ||
</code> | </code> | ||
- | Gramatica expresiilor genereaza conflicte cand uitam sa implementam asociativitatea si precedenta tokenilor. | + | Gramatica expresiilor generează conflicte când uitam să implementăm asociativitatea și precedența tokenilor. |
- | Sunt 2 moduri de a specifica precedenta si asociativitatea pentru o gramatica: implicit si explicit. Cand o specificam implicit trebuie sa introducem un simbol neterminal pentru fiecare nivel de precedenta. Iata mai jos solutia implicita: | + | Sunt 2 moduri de a specifica precedența și asociativitatea pentru o gramatică: implicit și explicit. Când o specificăm implicit trebuie să introducem un simbol neterminal pentru fiecare nivel de precedenta. Iată mai jos soluția implicită: |
<code C> | <code C> | ||
- | expr: expr '+' factor | + | expr |
- | | factor | + | : expr '+' factor |
+ | | factor | ||
; | ; | ||
- | factor: factor '*' term | + | factor |
- | | term | + | : factor '*' term |
+ | | term | ||
; | ; | ||
- | term: '-' term | + | term |
- | | ID | + | : '-' term |
- | | NUMBER | + | | ID |
- | ; | + | | NUMBER |
; | ; | ||
</code> | </code> | ||
- | Gramatica devine mai stufoasa, dar neambigua. Metoda explicita presupune folosirea explicita a regulilor de precedenta suportate de Bison. | + | Gramatica devine mai stufoasă, dar neambiguă. Metoda explicită presupune folosirea explicită a regulilor de precedență suportate de Bison. |
- | **Asociativitatea** si **precedenta** pot fi specificate in urmatorul mod: | + | **Asociativitatea** și **precedența** pot fi specificate în următorul mod: |
* Pentru asociativitate se pot folosi: ''%left'', ''%right'', ''%nonassoc'' | * Pentru asociativitate se pot folosi: ''%left'', ''%right'', ''%nonassoc'' | ||
- | * Precedenta operatorilor binari: | + | * Precedența operatorilor binari: |
- | * Se specifica asociativitatea folosind ''%left'' | + | * Se specifică asociativitatea folosind ''%left'' |
- | * Operatorii din acelasi grup au aceeasi precedenta, iar intre grupuri, precedenta creste in jos. | + | * Operatorii din același grup au aceeași precedență, iar între grupuri, precedența crește în jos. |
- | * Pentru a stabili precedenta operatorilor unari se foloseste ''%prec''. Acesta schimba precedenta unei reguli la precedenta tokenului urmator. | + | * Pentru a stabili precedența operatorilor unari se foloseste ''%prec''. Acesta schimbă precedența unei reguli la precedența tokenului următor. |
<code C> | <code C> | ||
%left ’+’ ’-’ | %left ’+’ ’-’ | ||
%left ’*’ ’/’ | %left ’*’ ’/’ | ||
... | ... | ||
- | expr : expr ’+’ expr | + | expr |
+ | : expr ’+’ expr | ||
| expr ’*’ expr | | expr ’*’ expr | ||
| ’-’ expr %prec ’*’ | | ’-’ expr %prec ’*’ | ||
Line 247: | Line 252: | ||
</code> | </code> | ||
- | **Exercitiu**: Incercati sa rezolvati conflictul shift/reduce pentru ''dangling else'' folosind reguli de precedenta. | + | **Exercițiu**: Încercați să rezolvați conflictul shift/reduce pentru ''dangling else'' folosind reguli de precedență. |
| | ||
- | ====== Instructiuni compilare si executie folosind flex si bison ====== | + | ====== Instrucțiuni compilare și execuție folosind flex și bison ====== |
<code bash> | <code bash> | ||
Line 259: | Line 264: | ||
</code> | </code> | ||
- | ====== Exerciții de laborator (10p) ====== | + | ====== Exerciții de laborator (13p) ====== |
În rezolvarea laboratorului folosiți arhiva de sarcini {{ :cpl:labs:lab02_simple_ops.zip | lab02_simple_ops.zip }} | În rezolvarea laboratorului folosiți arhiva de sarcini {{ :cpl:labs:lab02_simple_ops.zip | lab02_simple_ops.zip }} | ||
Line 270: | Line 275: | ||
===== Exercițiul 2 - variables (5p) ===== | ===== Exercițiul 2 - variables (5p) ===== | ||
- | Extindeți exercițul anterior astfel încât calculatorul să accepte instrucțiuni de atribuire și variabile și să poată evalua expresii care conțin variabile cărora le-a fost atribuită o valoare. Pentru a implementa această extensie, veți avea nevoie de o **tabelă de simboli** cu ajutorul căreia să țineți minte variabilele cărora li s-a atribuit o valoare și din care să citiți valoarea curentă a unei variabile în cazul în care variabila este operand al unei expresii. Tabela de simboli o puteți implementa folosind ''std::map''. | + | Extindeți exercițiul anterior astfel încât calculatorul să accepte instrucțiuni de atribuire și variabile și să poată evalua expresii care conțin variabile cărora le-a fost atribuită o valoare. Pentru a implementa această extensie, veți avea nevoie de o **tabelă de simboli** cu ajutorul căreia să țineți minte variabilele cărora li s-a atribuit o valoare și din care să citiți valoarea curentă a unei variabile în cazul în care variabila este operand al unei expresii. Tabela de simboli o puteți implementa folosind ''std::map<char *, int>''. |
+ | ===== Exercițiul 3 (bonus - 3p) ===== | ||
+ | Extindeți exercițiul anterior astfel încât calculatorul să accepte parantezarea expresiilor și operatorul unar minus (-), având în vedere prioritatea operatorilor. |