Het is belangrijk om debuggingsfaciliteiten te hebben, vooral als je aan het leren bent. Gelukkig kan YACC een hoop feedback geven. Deze feedback komt tegen de prijs van enige overhead, dus je moet enige switches geven om het aan te zetten.
Voeg als je je grammatica compileert, --debug en --verbose toe aan de YACC opdrachtregel. Voeg dit toe aan de C heading van je grammatica:
int yydebug = 1;
Dit zal het bestand `y.output' genereren die de gemaakte toestandsmachine uitlegt.
Als je nu de gegenereerde binary draait, zal het een *hoop* uitvoer geven over wat er gebeurt, inclusief in welke toestand de toestandsmachine op dit moment is, en welke tokens er worden gelezen.
Peter Jinks schreef een pagina op debugging die vaak voorkomende fouten en hun oplossingen bevat.
Intern draait je YACC parser een zogenaamde `toestandsmachine'. Zoals de naam zegt, is dit een machine die in verschillende toestanden kan verkeren. Dan zijn er regels die overgangen van de ene toestand naar de andere bepalen. Alles begint met de zogenaamde `root' regel die ik eerder vermeld heb.
Citaat uit de uitvoer van y.output van Voorbeeld 7:
state 0
ZONETOK , and go to state 1
$default reduce using rule 1 (commands)
commands go to state 29
command go to state 2
zone_set go to state 3
Standaard reduceert deze toestand met de `commands' regel. Dit is de voornoemde recursieve regel die `commands' definieert als zijnde opgebouwd uit individuele command statements, gevolgd door een puntkomma, gevolgd door mogelijk meer commands.
Deze toestand reduceert tot het iets tegenkomt dat het begrijpt, in dit geval een ZONETOK, d.i. het woord `zone'. Dan gaat het naar toestand 1, die het zone command verder afhandelt:
state 1
zone_set -> ZONETOK . quotedname zonecontent (rule 4)
QUOTE , and go to state 4
quotedname go to state 5
De eerste regel heeft een `.' om aan te geven waar we zijn: we hebben net een ZONETOK gezien en zoeken nu naar een `quotedname'. Blijkbaar begint een quotedname met een QUOTE, die ons naar toestand 4 stuurt.
Compileer om dit verder te volgen Voorbeeld 7 met de vlaggen uit de paragraaf Debuggen.
Steeds als YACC je waarschuwt over conflicten, kun je problemen verwachten.Het oplossen van deze conflicten schijnt iets van een kunstvorm te zijn die je een hoop over je taal kan leren. Misschien meer dan je wilde weten.
De problemen draaien om de vraag hoe een reeks tokens te interpreteren. Laten we aannemen dat we een taal hebben die deze commando's moet accepteren:
verwijder verwarming all
verwijder verwarming number1
Hiertoe definiëren we deze grammatica:
delete_heaters:
TOKDELETE TOKVERWARMING mode
{
deleteheaters($3);
}
mode: WOORD
delete_a_heater:
TOKDELETE TOKVERWARMING WOORD
{
delete($3);
}
Je ruikt misschien al moeilijkheden. De toestandsmachine begint met het woord `verwijder' te lezen, en moet dan op basis van het volgende token beslissen waar naartoe te gaan. Dit volgende token kan een mode zijn, die aangeeft hoe de verwarmingen te verwijderen, of de naam van een verwarming die te verwijderen is.
Het probleem is dat het volgende token in beide gevallen een WOORD is. YACC weet dus niet wat het moet doen. Dit leidt tot een `reduce/reduce' waarschuwing, en een waarschuwing dat de `delete_a_heater' node nooit bereikt zal worden.
In dit geval is het conflict gemakkelijk opgelost (door het eerste commando te hernoemen tot `verwijder verwarmingen all' of door `all' een apart token te maken), maar soms is het moeilijker. Het y.output bestand dat gegenereerd wordt als je YACC de --verbose vlag geeft kan enorm helpen.