Zotavenie pri syntaktických chybách

Ciele
  1. Oboznámiť sa s teoretickými základmi zotavenia.
  2. Prakticky si vyskúšať implementáciu zotavenia do prekladača.
  3. 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
  1. Ú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.
  2. 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.
    #define KeySet unsigned long int
    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 E 1 <<
    Viac o bitovom posune.

    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
  3. 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
  4. Ú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á funkciu error.

    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;
        }
    }
    
  5. Ú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.

  6. 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:
    
        program();
    
    zadajte výraz spolu so zotavením:
    
        program(E SEOF);
        if (lex_symbol != SEOF) {
            error("Ocakavany je koniec suboru", E SEOF);
        }
    
  7. Ú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 = ... ;
    
  8. Ú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 a error a nastavenie správnych množín kľúčových symbolov pre zotavenie. Môžete sa inšpirovať príkladom interpretátora uvedeným hore.
  9. Úloha: Podobným spôsobom pridajte kód zotavenia aj do osttných funkcií syntaktickej analýzy.
  10. 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
    
comments powered by Disqus