Avanti Indietro Indice

7. Debugging

Specialmente quando si sta imparando è importante avere strumenti per il debugging. Fortunatamente YACC può dare molte informazioni. Queste informazioni richiedono alcune attività addizionali, così è necessario fornire qualche opzione per abilitarle.

Quando si compila la grammatica si deve aggiungere --debug e --verbose alla linea di comando di YACC. Nella testata C della grammatica aggiungere la seguente definizione:

int yydebug=1;

Questo genererà il file 'y.output' che spiega la macchina degli stati che è stata creata.

Quando si lancia il binario generato, verrà mostrato *molto* di quello che sta succendendo, incluso a quale stato la macchina degli stati si trova attualmente, e quale categoria si sta leggendo.

Peter Jinks ha scritto una pagina in debugging che contiene alcuni errori comuni e come risolverli.

7.1 La macchina degli stati

Internamente, l'Analizzatore lessicale di YACC esegue una cosiddetta 'macchina degli stati'. Come il nome suggerisce è una macchina che può assumere molti stati. Ci sono poi regole che governano i passaggi da uno stato a un altro. Tutto parte con la cosiddetta regola che ho chiamato 'radice' menzionata precedentemente.

Per citare dall'uscita dell'Esempio 6 y.output:

stato 0

    0 $accept: . comandi $end

    $default        riduzione con la regola 1 (comandi)

    comandi         prosecuzione allo stato 1

stato 1

    0 $accept: comandi . $end
    2 comandi: comandi . comando PUNTOEVIRGOLA

    $end     shift e prosecuzione allo stato 2
    ZONETOK  shift e prosecuzione allo stato 3

    comando       prosecuzione allo stato 4
    imposta_zone  prosecuzione allo stato 5

Per default, questo stato riduce l'uso della regola 'comandi'. Questa è la regola ricorsiva menzionata prima che definisce 'comandi' come insieme di dichiarazioni di comandi individuali, seguita da un punto e virgola, seguito eventualmente da altri comandi.

Questo stato continua a ridurre la frase sino a quando non incontra qualcosa che è in grado di capire, in questo caso, un ZONETOK, cioè, la parola 'zone'. Va quindi allo stato 3, che ha ulteriormente a che fare con un comando di zona:

state 3

    4 imposta_zone: ZONETOK . nomefradoppiapici contenutozone

    DOPPIOAPICE  shift e prosecuzione allo stato 6

    nomefradoppiapici  prosecuzione allo stato 7

La prima linea contiene un '.' per indicare dove si è: si è appena visto un ZONETOK e si è in cerca di un 'nomefradoppiapici'. Apparentemente un nomefradoppiapici comincia con un DOPPIOAPICE, che manda la macchina allo stato 6.

Per andare oltre con l'esempio, utilizzare l'Esempio 6 che contiene le opzioni citate nella sezione Debugging, oppure aggiungete le opzioni all'Esempio 7, ricompilate, eseguite, ed esaminate y.output.

7.2 Conflitti: 'sposta/riduci', 'riduci/riduci'

Quando YACC avvisa di un conflitto, è probabile ci si trovi nei guai. La soluzione di questi conflitti sembra essere in qualche modo una forma d'arte che può insegnare molto a proposito del linguaggio in definizione. Più di quanto non si sarebbe voluto sapere.

I problemi girano intorno al modo d'interpretare una sequenza di categorie. Si supponga di definire un linguaggio che deve essere in grado di accettare entrambi questi comandi:

        cancella stufa tutte
        cancella stufa numero

Per farlo si definisce la grammatica:

        cancella_stufe:
                TOKCANCELLA TOKSTUFA modo
                {
                        cancellastufe($3);
                }

        modo:   PAROLA

        cancella_una_stufa:
                TOKCANCELLA TOKSTUFA PAROLA
                {
                        cancella($3);
                }

Si annusa già odore di guai. La macchina degli stati comincia leggendo la parola 'delete' e deve poi decidere dove andare basandosi sulla successiva categoria. Questa prossima categoria può essere un modo che specifica come cancellare le stufe, oppure il nome di una stufa da cancellare.

Il problema è che per entrambi i comandi la prossima categoria sarà una PAROLA. YACC quindi non ha la minima idea su cosa fare. Questo porta ad un avviso di 'riduzione/riduzione', e a un ulteriore avviso che il nodo 'cancella_una_stufa' non verrà mai raggiunto.

In questo caso il conflitto è risolto facilmente (cioè rinominando il primo comando in 'cancella_tutte_le_stufe', o facendo diventare una categoria separata 'tutte'), ma qualche volta è più difficile venirne fuori. Il file y.output generato quando si passa a yacc l'opzione --verbose può essere di grandissimo aiuto.


Avanti Indietro Indice