AttributengrammaticaEen attributengrammatica is een formele methode voor het specificeren van structurele eigenschappen van een programmeertaal door de productieregels van een contextvrije grammatica aan te vullen met regels die betrekking hebben op de (contextgevoelige) structuur van de taal. Attributengrammatica's kunnen gebruikt worden om structurele eigenschappen van een taal te specificeren die moeilijk, of helemaal niet, in een contextvrije grammatica vastgelegd kunnen worden. Voorbeelden hiervan zijn onder andere de eisen die een taal stelt wat betreft het gebruik van verschillende datatypen in expressies en de eis dat een variabele gedeclareerd moet worden voor deze gebruikt wordt. Syntax-directed translation en decorated syntax treesAttributengrammatica's zijn een formalisatie van het principe van syntax-directed translation. Bij syntax-directed translation worden aan de terminale en niet-terminale symbolen van een contextvrije grammatica een of meerdere attributen (eigenschappen) toegekend. Daarnaast worden de productieregels van de grammatica uitgebreid met regels om deze attributen te berekenen. Het resultaat wordt een syntax-directed definition genoemd en het gebruik ervan syntax-directed translation. Het parsen van een invoertekst met behulp van een grammatica levert een syntaxisboom op. De knopen in deze boom corresponderen met productieregels uit de grammatica. Bij syntax-directed translation gebruiken we deze knopen als records waarin een of meerdere attributen kunnen worden opgeslagen. Door de syntaxisboom in de juiste volgorde te doorlopen en daarbij voor iedere knoop de attribuutregels van de met die knoop corresponderende productieregel uit te voeren, worden alle attributen in de knopen ingevuld. Zo'n syntaxisboom waarin in de knopen extra informatie is opgeslagen wordt wel een decorated syntax tree of annotated syntax tree genoemd. Wanneer de regels in een syntax-directed definition geen neveneffecten hebben, is er sprake van een attributengrammatica. Hoewel bij het implementeren van programmeertalen niet altijd gebruik wordt gemaakt van een formele, expliciet geformuleerde attributengrammatica, wordt het achterliggende principe van syntax-directed translation in vrijwel iedere compiler gebruikt. VoorbeeldAls voorbeeld nemen we een attributengrammatica voor eenvoudige rekenkundige expressies, zoals "(3+6)*2". De volgende grammatica beschrijft de toegestane expressie's: Expr ::= Expr '+' Term
| Expr '-' Term
| Term
Term ::= Term '*' Factor
| Term '/' Factor
| Factor
Factor ::= '(' Expr ')'
| integer
We stellen een attributengrammatica op die de waarde van geldige expressies berekent. Hiertoe verbinden we met alle niet-terminale symbolen een waarde-attribuut, dat de numerieke waarde van het deel van de expressie dat door dat niet-terminale symbool gerepresenteerd wordt bevat. Bij alle productieregels specificeren we hoe de waarde dat bij het niet-terminale symbool aan de linkerkant hoort afgeleid kan worden uit de waarden van de symbolen aan de rechterkant. Voor het gemak nummeren we de onderdelen van productieregels waar nodig, schrijven we ze compleet uit en voeren we een startsymbool met een bijbehorende productieregel toe.
De uitkomst van een expressie kan nu berekend worden door een syntaxisboom van beneden naar boven (van de bladeren naar de wortel) te doorlopen en iedere keer dat we bij een knoop komen de betreffende regel te gebruiken om het attribuut te berekenen en in de knoop op te slaan. Als we alle knopen gehad hebben, met als laatste de wortel (de wortel correspondeert altijd met de Start-productie in onze voorbeeldgrammatica), dan bevat de wortel de uitkomst van de berekening. Hoewel het nogal omslachtig is om de waarde van expressies op deze manier te specificeren, kan dit toch een aantal voordelen hebben. We hebben nu een uitgangspunt voor het ontwerpen van een programma dat de uitkomst van dit soort expressies kan berekenen. Als we al een parser hebben die geldige expressies kan herkennen, kunnen we deze ook de berekening laten uitvoeren. Dat doen we door op iedere plaats waar de parser een productieregel 'herkent' de code die de bijbehorende attribuutregel uitvoert in te voegen. Als we nog geen parser hebben, kunnen we gebruikmaken van een parsergenerator. De meeste parsergenerators maken het mogelijk om acties te specificeren: fragmenten programmacode die aan een bepaalde productieregel gekoppeld worden en die uitgevoerd worden als de betreffende productieregel gebruikt wordt. We hoeven de attribuutregels in de attributengrammatica hierboven dan alleen nog om te zetten geldige code, waarna we deze als acties kunnen invoegen in de grammatica. De parsergenerator produceert vervolgens een programma dat expressies niet alleen herkent maar ook kan verwerken. Afgeleide, samengestelde en intrinsieke attributenWe kunnen de attributen die in een attributengrammatica gebruikt worden onderverdelen in drie soorten:
Samengestelde attributen komen in de praktijk het meest voor. Een attributengrammatica die, zoals ons voorbeeld hierboven, enkel uit samengestelde en intrinsieke attributen bestaat wordt een S-attributed grammar genoemd. Bij zo'n S-attributed grammatica kunnen alle attributen in een syntaxisboom berekend worden door de knopen van beneden naar boven (bottom-up) te verwerken. Bij grammatica's die niet S-attributed zijn, en die dus ook afgeleide attributen hebben, is de volgorde waarin de knopen verwerkt kunnen worden meestal minder duidelijk. In sommige gevallen kan er zelfs geen enkele bruikbare volgorde zijn, bijvoorbeeld als het attribuut van een of meer knopen direct of indirect afhangt van zichzelf, waardoor er een cirkel ontstaat. Zie ookBronnen, noten en/of referenties
|