![]() | Denne artikel omhandlersvært stof. Der er endnu ikke taget hensyn til ikke-eksperter. Begrundelsen kan findes pådiskussionssiden eller i artikelhistorikken. Du kan hjælpe ved at skrive en letforståelig indledning. (Lær hvordan og hvornår man kan fjerne denne skabelonbesked) |
EnLALR parser er etdatalogisk begreb for enparser. En parser er et program, som genkender kode skrevet i et programmeringssprog og derved gør det muligt at producere oversat kode – f.eks.C – eller en fortolker – f.eks.JavaScript.
LALR står forLook Ahead, Left to right, Rightmost derivation.
Look Ahead – ellerse fremad – betyder, at parseren ser på nogle af de efterfølgende symboler i koden, før den beslutter sig for, hvordan en konkret del af koden skal fortolkes.
Det forstås bedst ved et eksempel: I et program skal to tal lægges sammen; parseren har fået præsenteret kodestrengen:
Umiddelbart ser det ud som om, at det giver 42, men hvis det næste tegn er et gangetegn, så bliver resultatet et andet:
Fordi der kommer et gangetegn efter 25 må parseren vente med at udføre additionen til multiplikationen er udført. Hvis det næste tegn havde været et minus, som i:
ville det have været korrekt at udføre additionen først, og derefter udføre subtraktionen.
En LALR(1)-parser, som er i stand til at se 1 symbol frem, er i stand til at genkende situationer som disse.
Det er muligt at bygge parsere, som kan se flere tegn frem. En parser, der ser 2 tegn frem, ville hedde LALR(2). I moderne programmeringssprog foretrækker man dog at indrette syntaksen, så de kan parses med LALR(1)-parsere. Da de fleste LALR-parsere derfor er LALR(1) vil LALR ofte være synonymt med LALR(1).
En LALR parser kan produceres automatisk ud fra beskrivelsen af en syntaks;Yacc er et eksempel på et program, der gør det. Det er muligt, men ikke særligt nemt, at lave en LALR-parser uden at have et værktøj til det.
For at vise princippet vises her hvordan man kan lave en LALR-parser ud fra beskrivelsen af syntaksen for et yderst simpelt sprog. Det består af heltal og tegnene + (plus), – (minus), * (gange) og / (division). Inden LALR parseren får koden, er den blevet analyseret af en leksikalsk analysator; den fortolker teksten som tokens:
Nr | Regel | Terminatorer |
---|---|---|
r0 | Z →S | ◊ |
r1 | S →S ±P | ◊ ± |
r2 | S → ε | ◊ ± |
r3 | S →P | ◊ ± |
r4 | P →P ×N | ◊ ± × |
r5 | P →N | ◊ ± × |
De tre grundtokens,N, ± og ×, kan kombineres til de afledte tokensP (produkt),S (sum) ogZ (hele koden). Regel r5 siger f.eks., atP kan væreN (et heltal), og regel r4 siger, atP også kan være produktet afP ogN.
Der er desuden et pseudotoken, ε, som betyder, at et token kan være en tom plads. Det benyttes i regel r2, som siger, atS kan være en tom plads.
KolonnenTerminatorer viser hvilke tokens, der kan komme umiddelbart efter det token, der står på venstre side i reglen. Tegnet ◊ betyder, at der måske ikke kommer flere tegn. At der efterP (regel r4 og r5) kan komme et × ses af regel r4; at der også kan komme et ± ses af regel r3, som siger atP kan være etS og regel r1. Der kan ikke komme et × efterS, og der kan ikke komme nogen tegn efterZ.
Parseren fungerer som en endelig automat. Den skifter mellem et antal tilstande efterhånden som kildekodens tokens læses. Disse tilstande kan findes ud fra beskrivelsen af syntaksen.
Den første tilstand findes ved at gå ud fra, at hele kildekoden,Z, skal læses. Det skrives:
DaZ kan væreS i følge regel r0, er det muligvis samtidigS, der læses. Det kan skrives:
På denne måde ekspanderes listen vha. alle relevante regler. Det bliver til:
t0 | Regel | Handling |
---|---|---|
•Z | accept | |
r0 | Z → •S | t1 |
r1 | S → •S ±P | t1 |
r2 | S → • ε | t2 |
r3 | S → •P | t3 |
r4 | P → •P ×N | t3 |
r5 | P → •N | t4 |
NårZ er læst, er hele kildekoden accepteret; det angives med ordetaccept. For hvert af de øvrige mulige tokens, som kan læses som det næste, skal der være en ny tilstand. Der henvises til dem i kolonnenHandling.
De øvrige tilstande dannes efter de samme principper:
t1 | Regel | Handling |
---|---|---|
r0 | Z →S • | r0 |
r1 | S →S • ±P | t5 |
Når en regel, som i denne tilstand regel r0, er læst færdig, så indsættes reglen som reduktion i kolonnenHandling.
t2 | Regel | Handling |
---|---|---|
r2 | S → ε • | r2 |
t3 | Regel | Handling |
---|---|---|
r3 | S →P • | r3 |
r4 | P →P • ×N | t6 |
t4 | Regel | Handling |
---|---|---|
r5 | P →N • | r5 |
t5 | Regel | Handling |
---|---|---|
r1 | S →S ± •P | t7 |
r4 | P → •P ×N | t7 |
r5 | P → •N | t4 |
t6 | Regel | Handling | r4 | P →P × •N | t8 |
---|
t7 | Regel | Handling |
---|---|---|
r1 | S →S ±P • | r1 |
r4 | P →P • ×N | t6 |
t8 | Regel | Handling |
---|---|---|
r4 | P →P ×N • | r4 |
Informationerne omnæste tilstand ogregel fra tilstandene samles i en aktionstabel, som styrer parseren:
tilstand | ◊ | ± | × | N | S | P | Z |
---|---|---|---|---|---|---|---|
t0 | t2 | t2 | t4 | t1 | t3 | accept | |
t1 | r0 | t5 | |||||
t2 | r3 | r3 | |||||
t3 | r2 | r2 | t6 | ||||
t4 | r5 | r5 | r5 | ||||
t5 | t4 | t7 | |||||
t6 | t8 | ||||||
t7 | r1 | r1 | t6 | ||||
t8 | r4 | r4 | r4 |
Ofte har man to tabeller: enshift/reduce tabel svarende til de fire første kolonner (◊, ±, × ogN) og engo to tabel, svarende til de tre sidste kolonner (S,P ogZ).
I de tomme felter, som svarer til tokens som ikke forventes på det pågældende sted, kan man indsætte fejlmeddelelser. De er udeladt her for overskuelighedens skyld.
For at illustrere hvordan aktionstabellen bruges vises hvordan denne kildekode parses:
Kildekoden skal først læses af enleksikalsk analysator. Her genkendes de enkelte tekstelementer som tokens. Det genkendte token gemmes sammen med den tilsvarende tekst. Det kan vises som en kø af objekter:
Af hensyn til overskueligheden vises denne kø i denne forkortede udgave i den følgende oversigt:
nr | Stak | Kildekø | Handling | Operation | Reduktion | Beregning |
---|---|---|---|---|---|---|
0 | ±N ±N ×N | ←t0 | ||||
1 | t0 | ±N ±N ×N | [t0,±] : reducér | r2 :S → ε | S = 0 | |
2 | t0 | ±N ±N ×N | [t0,S] | ←t1 = [S,0] | ||
3 | t0 t1 | ±N ±N ×N | [t1,±] : læs | ←t5 = [±,-] | ||
4 | t0 t1 t5 | N ±N ×N | [t5,N] : læs | ←t4 = [N,3] | ||
5 | t0 t1 t5 t4 | ±N ×N | [t4,±] : reducér | t4→ | r5 :P →N | P = 3 |
6 | t0 t1 t5 | ±N ×N | [t5,P] | ←t7 = [P,3] | ||
7 | t0 t1 t5 t7 | ±N ×N | [t7,±] : reducér | t7→ t5→ t1→ | r1 :S →S ±P | S = 0 - 3 = -3 |
8 | t0 | ±N ×N | [t0,S] | ←t1 = [S,-3] | ||
9 | t0 t1 | ±N ×N | [t1,±] : læs | ←t5 = [±,+] | ||
10 | t0 t1 t5 | N ×N | [t5,N] : læs | ←t4 = [N,4] | ||
11 | t0 t1 t5 t4 | ×N | [t4,×] : reducér | t4→ | r5 :P →N | P = 4 |
12 | t0 t1 t5 | ×N | [t5,P] | ←t7 = [P,4] | ||
13 | t0 t1 t5 t7 | ×N | [t7,×] : læs | ←t6 = [×,*] | ||
14 | t0 t1 t5 t7 t6 | N | [t6,N] : læs | ←t8 = [N,21] | ||
15 | t0 t1 t5 t4 t6 t8 | [t8,◊] : reducér | t8→ t6→ t4→ | r4 :P →P ×N | P = 4 • 21 = 84 | |
16 | t0 t1 t5 | [t5,P] | ←t7 = [P,84] | |||
17 | t0 t1 t5 t7 | [t7,◊] : reducér | t7→ t5→ t1→ | r1 :S ±P | S = -3 + 84 = 81 | |
18 | t0 | [t0,S] | ←t1 = [S,81] | |||
19 | t0 t1 | [t1,◊] : reducér | t1→ | r0 :Z →S | Z = 81 | |
20 | t0 | [t0,Z] : accept | output 81 |