Grammatika teisendamisel analüüsiks sobivale (ükskõik, kas alt-üles või ülalt-alla) kujule tuleb tavaliselt juurde produktsioone ja mitteterminale, mis on vajalikud vaid analüüsi üheseks muutmiseks või kiirendamiseks. Selliste grammatikate alusel saadud derivatsioonipuudes on palju ülearuseid tippe. Näiteks grammatika
avaldis yksl avald_l
avald_l + yksl avald_l |
yksl tegur yksl_l
yksl_l * tegur yksl_l |
tegur (avaldis) | a
Selle avaldise struktuur, oluline informatsioon on esitatav palju väiksema puuga: - kõik ülejäänu on ülalesitatud puus ülearune.
Kuna kõik edasised tegevused põhinevad derivatsioonipuu läbimisel, aeglustaksid ülearused tipud transleerimise järgnevaid etappe - interpreteerimist või väljundkoodi genereerimist. Sellepärast toimub pärast süntaksianalüüsi puu moodustamist selle teisendamine nn abstraktse süntaksi puuks (AST - Abstract Syntax Tree). Abstraktse süntaksi puu kirjeldab vaid olulist informatsiooni, selle moodustamisel:
Tingimuslause if tingimus then käsud else käsud on kolme argumendiga funktsioon (kahe, kui else-osa puudub) ja selle abstraktne süntaks on
Vastavalt teisendatakse ka süntaksi kirjeldamiseks kasutatavat grammatikat. Kuna derivatsioonipuu on juba leitud, ei ole nüüd enam olulised analüüsiks kasutatud grammatikale esitatud nõuded - et ta oleks ühene ja võimaldaks kiiret analüüsi. Abstraktset süntaksit kirjeldatakse väga sageli mitmeste grammatikatega, näiteks aritmeetilise avaldise grammatika võib olla
avaldis avaldis + avaldis | avaldis - avaldis | avaldis * avaldis | avaldis / avaldis | Ident | Const
Abstraktse süntaksi puu on väga sobiv lähteteksti või selle osade (näiteks aritmeetiliste avaldiste) teisendamiseks nn poola kujule, s.t. kujule, kus funktsionaalse avaldise kirjutamisel loetletakse algul argumendid ja viimasena funktsioon (lõppjärjestus tavalise binaarsete aritmeetiliste avaldiste kirjutamisel kasutatava keskjärjestuse asemel), näiteks
a + b oleks poola kujul ab+
a + 2*b oleks poola kujul a 2 b * +
sin(2*a) + cos(3*b) oleks poola kujul 2 a * sin 3 b * cos +
if a > b then x := y + z oleks poola kujul a b > x y z + := if
( := ja if on kahe argumendiga funktsioonid) jne.Poola kuju on väga sageli kasutatav vahekuju, mida kasutatakse näiteks sisendprogrammi esitamiseks enne interpreteerimist. Poola kujul esitatud avaldise väärtuse arvutamine on lihtne realiseerida magasini abil: niipea, kui poola kujul esitatud avaldist vasakult paremale lugedes (s.t. avaldise sümboleid magasini surudes) on jõutud funktsionaalsümbolini, on selle all (ees) vastava funktsiooni argumendid ja nad kõik tuleb asendada vastava funktsiooni väärtusega; pärast seda võib protsessi jätkata.
Poola kuju saamiseks tuleb esitada avaldise abstraktse süntaksi
(s.t. avaldise semantika) puu lõppjärjestuses (endorder);
lõppjärjestuse algoritm on :
1. loetle vasakult paremale (lõppjärjestuses) alampuud;
2. loetle puu tipp.
Näiteks avaldise a + b * (c)
abstraktse süntaksi puu tippude läbimisel
lõppjärjestuses saame tippude järjekorraks
a b c * +
Eespool oli esitatud lihtsa programmeerimiskeele Pisi-Algoli (konkreetse) süntaksi grammatika; järgnevas on keelt veidi laiendatud, lubades if-lausel ka else-osa; avaldise struktuur on esitatud kujul, mis rahuldab LL(1)-tingimusi ja tagab aritmeetiliste operatsioonide +, -, *, / prioriteetide korrektse interpretatsiooni:
Programm Käsk;
Programm Käsk; Programm
Käsk Omistamine | Tingimuskäsk
| Tsükkel | Kirjutamine | Lugemine
Omistamine Identifikaator := Avaldis
Identifikaator Täht |
Indentifikaator (Täht | Number )
Avaldis Yksliige Avald_jatk
Avald_jatk λ | Add_op Avaldis
Yksliige Tegur Yksl_jatk
Tegur Identifikaator | Konstant |
(Avaldis) | Aste
Yksl_jatk λ | Mult_op Yksliige
Aste (Tegur ^ Tegur )
Add_op + | -
Mult_op * | /
Number 0| 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8
| 9
Täht A | B | C | D | E |
F | G | H
| I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z
Konstant Number | Number Konstant
Tingimuskäsk IF tingimus then_osa
then_osa THEN programm ENDIF | THEN
programm ELSE programm ENDIF
Tingimus Avaldis Tingimusop Avaldis
Tsükkel FOR Omistamine UNTIL
Avaldis DO Programm ENDLOOP
Tingimusop = | > | <
Kirjutamine WRITE Identifikaator
Lugemine READ Identifikaator
Selle (laiendatud) keele minimaalne abstraktse süntaksi grammatika võiks olla selline:
Programm ( Omistamine | Tingimuskäsk
| Tsükkel | Kirjutamine | Lugemine )+
Omistamine Identifikaator Avaldis
Identifikaator A..Z ( A..Z | 0..9 )*
Avaldis Yksliige ( [ + | - ] Avaldis)*
Yksliige [ Identifikaator | Konstant |
(Avaldis) | (Tegur ^ Tegur ) ] ( [ * | / ] Yksliige)*
Konstant ( 0..9 )+
Tingimuskäsk tingimus programm [
programm ]
Tingimus Avaldis Tingimusop Avaldis
Tsükkel Omistamine Avaldis Programm
Tingimusop = | > | <
Kirjutamine Identifikaator
Lugemine Identifikaator
Semantiliste tegevuste kirjeldamisel jääb selline minimaalne kuju siiski sageli veidi liiga minimaalseks. Näiteks ühekäsulise programmi
if a > 0 then x := 1 else y := 2
transleerimisel saame selle minimaalse abstrakste süntaksi kirjelduse põhjal kõrvaloleva puu. Kuid semantiliste tegevuste kirjeldamiseks jääb see puudulikuks; näiteks tingimuse a > 0 transleerimise järel peaks väljundisse genereerima kaks suunamislauset:
IF loogiline GOTO ...
GOTO ...
Siin loogiline on tingimuse arvutamise järel saadud loogiline väärtus, TRUE või FALSE ja esimene GOTO peaks juhtima esimese omistamise x := 1 transleerimise järel saadavate käskude algusesse, kuid teine GOTO - nende lõppu, s.t. omistamise y := 2 transleerimisel saadavate käskude algusesse.
Kuid kui tingimuses oleks lubatud ka loogilised operaatorid (AND, IF, ...), siis puu läbimisel (lõppjärjestuses) pole võimalik teada, et võrratus > on kogu if-lause tingimusosa kõige ülemine tipp ja siin peab genereerima suunamiskäsud - võibolla on (kõrgemal) veel loogilised operaatorid. Samasugune olukord tekib esimese omistamise x := 1 transleerimise järel - siin peaks genereerima GOTO-käsu, mis juhib teisest omistamisest y := 2 üle, kuid taas pole teada, kas if-lause then-osas ei järgne veel mingeid käske. Ka ülalesitatud puu struktuur pole otstarbekas: x := 1 transleerimise järel omistamisest y := 2 üle suunava GOTO käsu genereerimisel oleks tarvis teada else-osa, s.t. y := 2 transleerimisele järgneva järgmise (vaba) rea numbrit; selle saaks (lihtsamini) siis, kui else-käsud on then-osa alampuu. Sellepärast ei kasutatata abstrakse süntaksi puu moodustamisel alati "kõige minimaalsemat" versiooni; kiire väljundkoodi genereerimiseks on näiteks eespoolesitatud puu asemel märksa mugavam kõrvalolev puu. Vastavalt peaks muutma ka abstrakse süntaksi kirjeldust; selliseid praktika jaoks "sobivalt" minimaalseid AST-puid on esitatud vastavate programmikonstruktsioonide Antlr-i abil transleerimise näidetes.
Koos abstrakste süntaksi puu moodustamisega toimuvad ka nn semantilised kontrollid. Paljud programmeerimiskeeltes korrektse programmi omadused ei ole (lihtsalt) kirjeldatavad kontekstivaba grammatikaga, s.t. neid ei saa kontrollida nimede tabeli moodustamise ja süntaksianalüüsi ajal. Sellised on näiteks nõuded suunamistele: GOTO käsule järgnev märgend peab esinema programmis täpselt ühes kohas (üks kord) ja GOTO ei või suunata alamprotseduuri sisse. Selliste tingimuste kontroll nõuab sageli eraldi mälustruktuuride kasutamist ja/või abstraktse süntaksi puu läbimist.