- 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ý:Obr.: Stav zásobníka pri syntaktickej analýze výrazu s chybou -
Š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.Obr.: Ilustrácia prenosu hodnôt medzi pravidlamiPri 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; }
Obr.: Deklarácia typov prenášaných hodnôtNá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 ...
Obr.: Priradenie typu terminálnym a neterminálnym symbolom.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); }
Obr.: Priradenie hodnoty daného typu v lex. analyzátore (vstupný súbor pre Lex) -
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; };
Obr.: Deklarácia typov reálnočíselnej kalkulačkyÚ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á