- Pochopiť princíp zotavenia analyzatora pri syntaktickej chybe a spracovania chýb v nástrojoch Lex a Yacc.
- Naučiť sa špecifikovať typy hodnôt prenášaných v atribútoch symbolov jazyka.
Na tomto cvičení sa pozrieme na spracovanie chýb. Ak v analyzovanom vstupnom zdrojovom texte je chyba, teda tento vstup nezodpovedá gramatike definovanej vo vstupoch pre generátory lex a yacc, je možné jednoducho nahlásiť túto chybu a ukončiť analýzu. Je však užitočnejšie, ak analýza bude pokračovať a nájdu sa aj prípadné ďalšie chyby. Všimnite si, ako prekladač jazyka C vie nahlásiť viac existujúcich rozpoznaných syntaktických chýb, ktoré sa nachádzajú vo vstupnom zdrojovom súbore. Preto syntaktické analyzátory a prekladače implementujú zotavenie sa z chýb — pokračovanie v procese analýzy aj po výskyte chyby. Dôležité je, aby analyzátor nehlásil tzv. zavlečené chyby, ktoré v analyzovanom texte niesú, ale analyzátor ich nesprávne detekuje v dôsledku zlého zotavenia po predošlých chybách.
Ďalšou témou cvičenia je to, ako je možné prenášať v atribútoch symbolov hodnoty rôznych typov.
-
Spracovanie chýb pre generátory Lex-Yacc
Implicitnou reakciou na chybu v analyzátore generovanom nástrojom Yacc je volanie funkcie
yyerror("syntax error")
a ukončenie práce analyzátora, ak nie je možné zotavenie. Stručné zhrnutie implementácie zotavenia v lexikálnom a syntaktickom analyzátore je uvedené nižšie.Princíp zotavenia pri lexikálnej chybe:
- CHYBA = vstup nezodpovedá žiadnemu symbolu,
- použiť vypúšťacie pravidlo:
. {yyerror("nedovoleny znak");}
Princíp zotavenia pri syntaktickej chybe:
- CHYBA = vstup nezodpovedá pravidlám gramatiky.
- Použiť v pravidlách, kde možno očakávať chyby, preddefinovaný terminálny symbol error.
- Pri zistení chyby je testovaná možnosť pre doteraz spracované pravidlá (neterminálne symboly) využitia symbolu error (či existuje možnosť jeho presunu do zásobníka)
- Ak je to možné — zotavenie, inak ukončenie práce analyzátora
- Po zotavení zostáva analyzátor v stave nehlásenia chýb, kým správne nespracuje tri vstupne symboly
Príklad: Majme gramatiku:
kalkul: e | kalkul vypocet CR | kalkul CR | kalkul error CR ; vypocet : vyraz vyraz : vyraz PLUS vyraz | vyraz DELENE vyraz | CISLO ;
Pre vstup "
2++3 CR 4/5 CR
" obsahujúci chybu bude priebeh syntaktickej analýzy nasledovný: -
Špecifikácia typu prenášaných hodnôt
Pri analýze textu sú symbolom gramatiky priradené hodnoty - atribúty symbolov, ktoré sú prenášané medzi akciami priradenými k jednotlivým pravidlám. Atribúty symbolov sú realizované v generátoroch lex a yacc pomocou tzv. pozičných premenných $1,$2,..$$. V príklade z minulého cvičenia sa prenášané hodnoty využívali na uchovanie medzivýsledku výpočtu výrazu.
Hodnota priradená do špeciálnej premennej
$$
sa priraďuje symbolu na ľavej strane pravidla gramatiky. Následne, ak sa tento symbol vyskytne na pravej strane niektorého iného pravidla, daná hodnota bude prístupná v špeciálnej premennej$i
, pričom i je číslo dané poradím daného symbolu na pravej strane pravidla. Lexikálnym symbolom sú hodnoty priraďované v lexikálnom analyzátore tak, že sa zapíšu do premennej yylval.Pri implementácii prekladača môže byť potrebné prenášať so symbolmi hodnoty rôznych typov. Definícia typu prenášaných hodnôt sa vo vstupnom súbore Yacc zadáva pomocou direktívy %union, ktorá má syntax obdobnú deklarácií union v jazyku C. Vymenujú sa tam všetky typy hodnôt ktoré sa budú používať v programe a sú im priradené mená.
%union{ TYP1 MENO1; ... TYPn MENOn; }
Následne je potrebné priradiť typ terminálnym symbolom tak, že sa meno typu uvedie v direktíve %token. Na priradenie typu neterminálnym symbolom sa používa direktíva %type.
%token <meno> term1 term2 ... %type <meno> net1 net2 ...
V lexikálnom analyzátore sa potom s premennou yylval, ktorá slúži na prenos hodnôt lexikálnych symbolov, pracuje ako s hodnotou typu union. To znamená, že je potrebné pristupovať k jej príslušnej zložke, ktorá zodpovedá potrebnému typu.
%{ extern YYSTYPE yylval; %} %% symbol { yylval.meno = hodnota; return(KOD); }
-
V pripravenom zdrojovom kóde kalkulačky je interpretér z minulého cvičenia upravený tak, aby dokázal pracovať nie len s celočíselnými ale aj s reálnymi hodnotami (typu double).
%union { int ihod; double rhod; struct { int jeint; double hod; } vhod; };
Úloha: Preštudujte si implementáciu reálnočíselnej kalkulačky. Ako sa v akciách vo vstupnom súbore pre Yacc pristupuje k prenášaným hodnotám? Používa sa identifikátor typu (napríklad$1.ihod
)?Úloha: Upravte calc2.l a calc2.y, aby kalkulačka umožňovala:int(vyraz)
, kde výsledkom bude celočíselná hodnota výrazux@y
, kde výsledkom bude y-ta (y — celé číslo) mocnina x (celé alebo reálne číslo)- Relačné operátory
<
,<=
,>
,>=
,==
,!=
, kde výsledkom bude 0 alebo 1 x ? y : z
, kde výsledkom bude y, ak hodnota x bude nenulová a výsledkom bude z, ak hodnota x bude nulová