Ciele
- Oboznámiť sa s teoretickými základmi zotavenia.
- Prakticky si vyskúšať implementáciu zotavenia do prekladača.
- Implementovať zotavenie do príkladu z cvičenia 2.
Úvod
-
Úlohou dnešného cvičenia bude upraviť kód z minulých cvičení tak, aby fungovalo zotavenie.
Postup
-
Úlohou dnešného cvičenia bude upraviť kód predchádzajúceho cvičenia tak, aby fungovalo zotavenie.Úloha: Preštudujte si teoretické základy zotavenia po syntaktických chybách.
-
Na to, aby sme mohli realizovať zotavenie, potrebujeme definovať typ množiny kľúčov. Množiny kľúčov budeme v zotavení používať pre definovanie očakávaných symbolov. Na reprezentáciu množiny sa bude používať celé číslo. Každý bit čísla bude zodpovedať jednému zo symbolov, pričom ak je tento symbol prítomný v množine, tak príslušný bit má hodnotu 1.
Množiny kľúčov budeme definovať prostredníctvom operácie bitového posunu čísla 1 doľava o n bitov (kde n predstavuje hodnotu symbola zo zoznamu enum Symbols{...}):#define KeySet unsigned long int
Viac o bitovom posune.#define E 1 <<
Bitový posun aplikujeme na prvky zo zoznamu symbolov Symbol (hodnota každého prvku v zozname Symbol je číselná, predstavuje index prvku v enumeračnom poli). Príklad:KeySet A = E VALUE; ⇒ 00000001 KeySet B = E ID; ⇒ 00000010 KeySet C = E MUL; ⇒ 01000000
-
Množinové operácie budeme realizovať prostredníctvom operácií bitového súčtu (|) a bitového súčinu (&). Viac o bitových operáciách.
Realizácia množinových operácií:- Zjednotenie množín
- A = {+} ∪ {-} ⇔ KeySet A = (E PLUS) | (E MINUS)
- B = A ∪ {^} ⇔ KeySet B = A | (E POWER)
- Prienik množín
- A = {+} ∩ {-} ⇔ KeySet A = (E PLUS) & (E MINUS)
- B = A ∩ {-} ⇔ KeySet B = A & (E MINUS)
- Zistenie, či sa prvok {+} nachádza v množine A:
- KeySet A = (E PLUS) | (E MINUS)
- A & (E MINUS) != 0 ⇒ true
- A & (E POWER) = 0 ⇒ false
-
Úloha: Doplňte do kódu funkcie check a error:
void error(const char *msg, KeySet K) { fprintf(stderr, "CHYBA: %s\n", msg); /* chybové hlásenie */ /* preskočenie nekľučových symbolov */ while (!(E lex_symbol & K)) next_symbol(); } void check(const char *msg, KeySet K) { if (!(E lex_symbol & K)) error(msg, K); }
Funkcia
check
sa používa na kontrolu, či aktuálny symbol zodpovedá tomu, čo očakávame. Ak je aktuálny symbol nesprávny, zavolá funkciuerror
.Funkcia
error
sa používa na výpis chybových hlásení a na preskočenie nesprávnych symbolov na vstupe, až kým sa nenájde očakávaný symbol (t.j. symbol, ktorý sa nachádza v množine kľúčov K).Úloha: Upravte funkciu match, aby podporovala zotavenie.int match(const Symbol expected, KeySet K) { if (lex_symbol == expected) { int attr = lex_attr; next_symbol(); return attr; } else { char *msg = malloc(100); snprintf(msg, 100, "Ocakavany symbol %s, namiesto toho sa vyskytol %s", symbol_name(expected), symbol_name(lex_symbol)); error(msg, K); return 0; } }
-
Úloha: Upravte funkcie interpretátora tak, aby argumentom každej funkcie bola množina kľúčov KeySet K:
void expr(KeySet K) {...} void term(KeySets K) {...} ...
Pri volaní týchto funkcií zatiaľ použite ako argument len hodnotu množiny
K
volajúcej funkcie. -
Je potrebné upraviť aj hlavnú funkciu main tak, aby zotavenie bolo realizované i vzhľadom na symbol ukončenia vstupu:Úloha: Namiesto pôvodného volania funkcie pre preklad programu:
zadajte výraz spolu so zotavením:program();
program(E SEOF); if (lex_symbol != SEOF) { error("Ocakavany je koniec suboru", E SEOF); }
-
Úloha: Doplňte do kódu definované množinu kľúčov H pre pravidlo Term:
KeySet H_Term = E VALUE | E ID | E LPAR;
Úloha: Doplňte množiny H_Expr a H_Mul pre pravidla Expr a Mul. Inšpirujte sa návodom.KeySet H_Expr = ... ; KeySet H_Mul = ... ;
-
Úloha: Určte množiny kľúčových symbolov pre zotavenie v príklade interpretátora (označené komentármi s písmenami).
// Term -> value | "(" Expr ")" int term(KeySet K) { int value; check("Ocakava sa hodnota alebo zatvorka", /* ??? */); // a) switch (lex_symbol) { case VALUE: value = lex_attr; next_symbol(); break; case LPAR : next_symbol(); value = expr(/* ??? */); // b) match(RPAR, K); break; default: error("Chyba operand", /* ??? */); // c) } return value; } // Expr -> Mul { ("+" | "-") Mul } int expr(KeySet K) { int leftOp, rightOp; Symbol operator; leftOp = mul(E PLUS | E MINUS | H_Mul | K); check("Ocakava sa operator + alebo -", E PLUS | E MINUS | H_Mul | K); while ((E lex_symbol) & E PLUS | E MINUS | H_Mul) { if ((E lex_symbol) & (E PLUS | E MINUS)) { operator = lex_symbol; next_symbol(); } else error("Chyba operator + alebo -", /* ??? */); // d) rightOp = mul(/* ??? */); // e) switch (operator) { case PLUS: leftOp = leftOp + rightOp; break; case MINUS: leftOp = leftOp - rightOp; break; default: assert("Neocakavany operator v expr()"); } check("Ocakava sa operator + alebo -", /* ??? */); // f) } return leftOp; }
Poznámka: Pomôžte si návodom a pre inšpiráciu môžete použiť aj príklady.Úloha: Doplňte svoj kód prekladača tak, že doň doplníte volanie funkciícheck
aerror
a nastavenie správnych množín kľúčových symbolov pre zotavenie. Môžete sa inšpirovať príkladom interpretátora uvedeným hore. -
Úloha: Podobným spôsobom pridajte kód zotavenia aj do osttných funkcií syntaktickej analýzy.
-
Príklad programu pre zotavenie s implementovanými operátormi + a - si môžete pozrieť po spustení súboru zotavenie.exe.
Doplňujúce úlohy
Úloha: Doplňte do výpisov chybových hlásení zotavenia výpis pozície, na ktorej bola chyba nájdená. Napr.:
CHYBA na pozícii 3: Ocakava sa cislo alebo lava zatvorka '('
Úloha: Implementujte do zotavenia funkciu opätovného výpisu vstupu používateľa s označením všetkých nájdených chýb a ich počtu. Napr.:
3 + f d 4 - ( 2 f d ^ 4 \0
^ ^ ^ ^ ^
Pocet chyb: 5