follow(a) first(α j ) x

Samankaltaiset tiedostot
Esimerkki 47. Kieli {a i b j c k : i = j tai j = k} on luonnostaan moniselitteinen.

Vasen johto S AB ab ab esittää jäsennyspuun kasvattamista vasemmalta alkaen:

jäsentämisestä TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 27. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

Jäsennysalgoritmeja. TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 29. syyskuuta 2009 TIETOTEKNIIKAN LAITOS. Jäsennysalgoritmeja

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 10. kesäkuuta 2013

jäsennyksestä TIEA241 Automaatit ja kieliopit, syksy 2016 Antti-Juhani Kaijanaho 29. syyskuuta 2016 TIETOTEKNIIKAN LAITOS Kontekstittomien kielioppien

TIEA241 Automaatit ja kieliopit, kevät 2011 (IV) Antti-Juhani Kaijanaho. 29. huhtikuuta 2011

Laskennan mallit (syksy 2010) Harjoitus 8, ratkaisuja

jäsentäminen TIEA241 Automaatit ja kieliopit, syksy 2015 Antti-Juhani Kaijanaho 26. marraskuuta 2015 TIETOTEKNIIKAN LAITOS

ICS-C2000 Tietojenkäsittelyteoria

Yhteydettömät kieliopit [Sipser luku 2.1]

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. kesäkuuta 2013

T Syksy 2002 Tietojenkäsittelyteorian perusteet Harjoitus 8 Demonstraatiotehtävien ratkaisut

S BAB ABA A aas bba B bbs c

Testaa: Vertaa pinon merkkijono syötteeseen merkki kerrallaan. Jos löytyy ero, hylkää. Jos pino tyhjenee samaan aikaan, kun syöte loppuu, niin

Täydentäviä muistiinpanoja jäsennysalgoritmeista

Yhteydettömän kieliopin jäsennysongelma

Attribuuttikieliopit

ICS-C2000 Tietojenkäsittelyteoria Kevät 2016

LR-jäsennys. Antti-Juhani Kaijanaho. 3. lokakuuta 2016

Rajoittamattomat kieliopit (Unrestricted Grammars)

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 16. helmikuuta 2012

Algoritmit 1. Demot Timo Männikkö

Algoritmit 1. Demot Timo Männikkö

Jäsennys. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Pinoautomaatit. TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 6. kesäkuuta 2013 TIETOTEKNIIKAN LAITOS. Pinoautomaatit.

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

2. Yhteydettömät kielet

Esimerkki 2.28: Tarkastellaan edellisen sivun ehdot (1) (3) toteuttavaa pinoautomaattia, jossa päätemerkit ovat a, b ja c ja pinoaakkoset d, e ja $:

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 3. lokakuuta 2016

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 29. syyskuuta 2009

Ei-yhteydettömät kielet [Sipser luku 2.3]

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

ITKP102 Ohjelmointi 1 (6 op)

Rekursiivinen Derives on periaatteessa aivan toimiva algoritmi, mutta erittäin tehoton. Jos tarkastellaan esim. kieliopinpätkää

T Syksy 2006 Tietojenkäsittelyteorian perusteet T Harjoitus 7 Demonstraatiotehtävien ratkaisut

Olkoon G = (V,Σ,P,S) yhteydetön kielioppi. Välike A V Σ on tyhjentyvä, jos A. NULL := {A V Σ A ε on G:n produktio};

Ohjelmoinnin perusteet Y Python

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2015

TAMPEREEN TEKNILLINEN YLIOPISTO

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

Täydentäviä muistiinpanoja Turingin koneiden vaihtoehdoista

TIEA241 Automaatit ja kieliopit, kesä Antti-Juhani Kaijanaho. 29. toukokuuta 2013

4. Tehtävässä halutaan todistaa seuraava ongelma ratkeamattomaksi:

Ohjelmoinnin perusteet Y Python

TIEA341 Funktio-ohjelmointi 1, kevät 2008

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 2. helmikuuta 2012

uv n, v 1, ja uv i w A kaikilla

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Tietorakenteet ja algoritmit

TAMPEREEN TEKNILLINEN YLIOPISTO

ICS-C2000 Tietojenkäsittelyteoria Kevät 2016

Algoritmit 1. Luento 4 Ke Timo Männikkö

Ohjelmoinnin perusteet Y Python

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 16. marraskuuta 2015

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 12. tammikuuta 2012

Rajoittamattomat kieliopit

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 5. marraskuuta 2015

δ : (Q {q acc, q rej }) (Γ k {, }) Q (Γ k {, }) {L, R}.

Ohjelmoinnin perusteet Y Python

ITKP102 Ohjelmointi 1 (6 op)

Lisää pysähtymisaiheisia ongelmia

Ohjelmoinnin perusteet Y Python

Algoritmit 2. Luento 7 Ti Timo Männikkö

Monipuolinen esimerkki

Kontekstittomien kielten jäsentäminen Täydentäviä muistiinpanoja TIEA241 Automaatit ja kieliopit, syksy 2016

M =(K, Σ, Γ,, s, F ) Σ ={a, b} Γ ={c, d} = {( (s, a, e), (s, cd) ), ( (s, e, e), (f, e) ), (f, e, d), (f, e)

Vaihtoehtoinen tapa määritellä funktioita f : N R on

811120P Diskreetit rakenteet

7.4 Sormenjälkitekniikka

Rekursio. Funktio f : N R määritellään yleensä antamalla lauseke funktion arvolle f (n). Vaihtoehtoinen tapa määritellä funktioita f : N R on

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

Tehtävän V.1 ratkaisuehdotus Tietorakenteet, syksy 2003

5.5 Jäsenninkombinaattoreista

Algoritmit 1. Luento 3 Ti Timo Männikkö

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

LOAD R1, =2 Sijoitetaan rekisteriin R1 arvo 2. LOAD R1, 100

Algoritmit 2. Luento 14 Ke Timo Männikkö

811120P Diskreetit rakenteet

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Ohjelmoinnin peruskurssien laaja oppimäärä

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

3. Hakupuut. B-puu on hakupuun laji, joka sopii mm. tietokantasovelluksiin, joissa rakenne on talletettu kiintolevylle eikä keskusmuistiin.

Pinoautomaatit. Pois kontekstittomuudesta

Ohjelmointi 1 C#, kevät 2014, 2. uusintatentti NIMI:

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

tään painetussa ja käsin kirjoitetussa materiaalissa usein pienillä kreikkalaisilla

TIEA241 Automaatit ja kieliopit, kevät Antti-Juhani Kaijanaho. 26. tammikuuta 2012

Täydentäviä muistiinpanoja kontekstittomien kielioppien jäsentämisestä

Ohjelmoinnin peruskurssi Y1

Imperatiivisen ohjelmoinnin peruskäsitteet. Meidän käyttämän pseudokielen lauseiden syntaksi

Opiskelijan pikaopas STACK-tehtäviin. Lassi Korhonen, Oulun yliopisto

13. Loogiset operaatiot 13.1

Laskennan teoria (kevät 2006) Harjoitus 3, ratkaisuja

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 9. lokakuuta 2016

Todistus: Aiemmin esitetyn mukaan jos A ja A ovat rekursiivisesti lueteltavia, niin A on rekursiivinen.

Datatähti 2019 alku. task type time limit memory limit. A Kolikot standard 1.00 s 512 MB. B Leimasin standard 1.00 s 512 MB

Ongelma(t): Miten jollakin korkeamman tason ohjelmointikielellä esitetty algoritmi saadaan suoritettua mikro-ohjelmoitavalla tietokoneella ja siinä

Transkriptio:

Tästä ensimmäisestä LL(1)-ehdosta (14) seuraa erityisesti, että korkeintaan yksi välikkeen A säännöistä voi tuottaa tyhjän merkkijonon ε eli tehdä välikkeestä A tyhjentyvän (eli nollautuvan). Toinen osa LL(1)-ehtoa koskee vain tyhjentyviä välikkeitä: Olkoon välike A N tyhjentyvä ja sen säännöistä viimeinen eli A α k se ainoa, joka voi tuottaa tyhjän merkkijonon ε. Silloin ehto vaatii, että follow(a) first(α j ) = (15) kaikilla muilla sen säännöillä 1 j < k. Nimittäin jos olisi jokin follow(a) first(α j ) x niin kumpaa säännöistä j vaiko k pitäisi käyttää tällä x? Kielioppi G on yleisessä LL(1)-muodossa, jos sen kaikki välikkeet ja säännöt täyttävät molemmat ehdot (14) ja (15). LL(1)-kielioppi ei voi olla moniselitteinen. LL(1)-kielioppi ei voi sisältää vasenta rekursiota. Kun kielioppi G on tätä yleistä LL(1)-muotoa, niin sille voidaan laatia rekursiivisesti etenevä jäsentäjä seuraavin periaattein: Pidetään yllä muuttujassa next seuraavaa syötemerkkiä. error(...) tarkoittaa lopeta koko rekursiivinen jäsennys virheilmoitukseen... Käytännön ohjelmoinnissa se voisi vaikkapa nostaa poikkeuksen (exception). Tehdään tässä esimerkissä sellainen jäsentäjä, joka palauttaa arvonaan vastaavan jäsennyspuun. Tätä kusutaan ennustavaksi (englanniksi predictive ) jäsentämiseksi, koska jäsentäjä osaa ennustaa oikein, mitä produktiota seuraavaksi pitää soveltaa, lukematta syötettä enempää kuin nextin verran eteenpäin. Jokaiselle päätesymbolille a Σ kirjoitetaan oma aliohjelma: a: 1 if next = a 2 then next lue seuraava syötemerkki 3 return uusi lapseton solmu nimeltään a 4 else error(tässä kohdasta olisi pitänyt olla a) Jokaiselle välikkeelle A N kirjoitetaan oma aliohjelma. Jos A ei ole tyhjentyvä, niin tämä aliohjelma on: 144

A: 1 if next first(α 1 ) then haara(α 1 ) 2 elseif next first(α 2 ) then haara(α 2 ) 3 elseif next first(α 3 ) then haara(α 3 ). elseif next first(α k ) then haara(α k ) else error(tästä kohdasta olisi pitänyt alkaa A) Huomaa, että nämä first-joukot ovat vakioita, jäsentäjä ei siis laske niitä. Niiden arvothan on jo laskettu LL(1)-ehtoa (14) testattaessa. Jokainen haara(x 1 X 2 X 3...X m ) on oma ohjelmanpätkänsä 1 y 1 X 1 2 y 2 X 2 3 y 3 X 3. y m X m return uusi solmu nimeltään A lapsinaan y 1, y 2, y 3,...,y m joka siis 1. ensin kutsuu rekursiivisesti muita jäsentäjän aliohjelmia X 1, X 2, X 3,...,X m oikeassa järjestyksessä 2. sitten palauttaa tuloksenaan jäsennyspuun, jonka juurena on nykyinen välike A ja sen lapsina näiden kutsujen palauttamat puut. (Tai jos jäsentimen halutaan tekevän jotakin muuta kuin jäsennyspuun, niin sitten tekee mitä halutaan pohjautuen siihen, mitä rekursiokutsut ovat ensin tehneet ja palauttaneet.) Jos välike A on tyhjentyvä niin vain sen viimeinen sääntö A α k tuottaa tyhjän merkkijonon ε. Silloin sen aliohjelma päättyykin. elseif next first(α k 1 ) then haara(α k 1 ) else haara(α k ) eli tämä tyhjentyvä viimeinen haara siirtyykin elseen errorin tilalle. Toisin sanoen, jos nextin mukaan kyseessä ei ollut mikään tyhjentymättömistä haaroista A α 1 α 2 α 3... α k 1 niin sitten ainoa mahdollisuus on tyhjentyvä haara A α k. Koko jäsentäjän pääohjelmaksi tulee 145

1 next lue syötteen ensimmäinen merkki 2 τ S eli kutsutaan lähtösymbolia vastaavaa aliohjelmaa 3 if next = EOF 4 then return näin rakennettu koko syötteen jäsennyspuu τ 5 else error(syötteen olisi pitänyt loppua tähän kohtaan) Usein halutaan sellainen jäsennysohjelma, joka ei pysähdy heti ensimmäiseen erroriin, vaan jatkaa eteenpäin, ja raportoi muitakin syötteessä olevia virheitä. Silloin kirjoitetaan kunkin tyhjentymättömän välikkeen A aliohjelman päättävän errorin tilalle tulosta(tästä kohdasta olisi pitänyt alkaa A); while next follow(a) do next lue seuraava syötemerkki; return uusi lapseton virhesolmu nimeltään A joka siis selaa ohi tämän virheellisen A, ja jatkaa jäsennystä sitä seuraavasta merkistä. Jokaiselle välikkeelle A N määritellään first(a) = first(α 1 ) first(α 2 ) first(α 3 )... first(α k ) (16) eli sen first-joukko koostuu kaikista sen sääntöjen oikeiden puolten α i firstjoukoista. Tällaisen oikean puolen α V first-joukko lasketaan puolestaan seuraavasti: Jos α = ε, niin first(α) = {ε. Jos α on muotoa b... jollakin päätemerkillä b Σ, niin first(α) = {b. Jos α on muotoa Bβ, jossa välike B ei ole tyhjentyvä, niin first(α) = first(b) joka taas lasketaan kuten yhtälössä (16). Jos α on muotoa Bβ, jossa välike B on tyhjentyvä, niin eli edetään eteenpäin jonossa α. first(α) = first(b) \ {ε first(β) Kaiken vasemman rekursion poisto takaa, ettei tämä ole kehämääritelmä. Välikkeiden follow-joukot voidaan puolestaan laskea toistamalla seuraavia sääntöjä, kunnes mikään joukko ei enää kasva: Lisää EOF lähtösymbolin S joukkoon follow(s). 146

Jos kieliopissa on jokin sääntö muotoa A αbβ, niin lisää joukkoon follow(b) kaikki joukon first(β) päätesymbolit. (Eli kaikki muut sen alkiot, mutta ei mahdollista tyhjää merkkijonoa ε). Jos kieliopissa on jokin sääntö muotoa A αbβ jossa ε first(β) niin lisää joukkoon follow(b) kaikki joukon follow(a) alkiot. Esimerkki 62. Esimerkin 59 tekijöidyssä kieliopissa tarvitaan LL(1)-jäsentäjää varten seuraavat joukot: first(t) = {a, ( first(e ) = {+,, ε first(e) = first(t) follow(e ) = follow(e) = {EOF, ). Näiden perusteella voidaan kirjoittaa jäsentäjä edellä kuvattuun tapaan. Lyhennetään koodia kirjoittamalla yksi yhteinen aliohjelma kaikille päätemerkeille b { +,, (, ),a: Terminaali(b): 1 if next = b 2 then next lue seuraava syötemerkki 3 return uusi lapseton solmu nimeltään b 4 else error(tässä kohdassa olisi pitänyt olla b) Pääohjelmaksi tulee: 1 next lue ensimmäinen syötemerkki 2 τ E 3 if next = EOF 4 then return τ 5 else error(syötteen olisi pitänyt loppua tähän kohtaan) Välikkeen E aliohjelmaksi tulee: E: 1 if next { (, a then y 1 T y 2 E return uusi solmu nimeltään E ja lapsinaan y 1, y 2 2 else error(tästä kohdasta olisi pitänyt alkaa E) Välikkeen E aliohjelmaksi tulee: 147

E : 1 if next { + then y 1 Terminaali( + ) y 2 E return uusi solmu nimeltään E ja lapsinaan y 1, y 2 2 elseif next { then y 1 Terminaali( ) y 2 E return uusi solmu nimeltään E ja lapsinaan y 1, y 2 3 else return uusi lapseton solmu nimeltään E (Tässä siis on haara säännölle E ε.) Välikkeen T aliohjelmaksi tulee: T: 1 if next {a then y 1 Terminaali(a) return uusi solmu nimeltään T ja lapsenaan y 1 2 elseif next { ( then y 1 Terminaali( ( ) y 2 E y 3 Terminaali( ) ) return uusi solmu nimeltään T ja lapsinaan y 1, y 2, y 3 3 else error(tästä kohdasta olisi pitänyt alkaa T) Tätä systemaattisesti kirjoitettua jäsennintä voi selvästi vielä parannella paikallisin muutoksin: esimerkiksi aliohjelman T rivillä 2 tarkastetaan kahdesti, että next on (. Tehdään siis parempi C-pseudokoodilla. void E() { tulosta(e TE ) T(); E (); void E () { if (next == + ) { tulosta(e +E) E(); else if (next == - ) { tulosta(e -E) E(); else tulosta(e ε) 148

void T() { if (next == a ) { tulosta(t a) else if (next == ( ) { tulosta(t (E)) E(); if (next ) ) error(sulkeva sulku puuttuu); else error(t ei voi alkaa merkillä next); Pääohjelma käynnistää ja päättää jäsennyksen: E(); if (next EOF) error(tässä piti olla EOF). Katsotaan esimerkki 63 sen toiminnasta. Sitten korvataan sen tulosteet yksinkertaisella koodingeneroinnilla. Esimerkki 63. Syötejonon a-(a+a) jäsennys tulostaa: E TE T a E -E E TE T (E) E TE T a E +E E TE T a E ε E ε Tulostus vastaa vasenta johtoa: E TE ae a E a TE a (E)E a (TE )E a (ae )E a (a + E)E a (a + TE )E a (a + ae )E a (a + a)e a (a + a). Oikeassa ohjelmassa tulosta-komennot voivat tehdä jotain hyödyllisempää (kuten laskea lausekkeen arvoa, generoida koodia,...). 149

// Lelukääntäjä: tuottaa konekoodia edellisen kieliopin // mukaisten lausekkeiden arvon laskemiseksi; tulos rekisteriin // r1... EI ole testattu, vastuu lukijalla: void Ep() { if(next == + ) { T(); printf("pop r1\npop r2\nadd r1, r2\npush r1\n"); Ep(); else if(next == - ) { T(); printf("pop r2\npop r1\nsub r1, r2\npush r1\n"); Ep(); void T() { if(numero_tai_muuttuja(next)) { printf("push % else if(next == ( ) { T(); Ep(); if(next!= ) ) printf("virhe: piti olla loppusulku\n"); else printf("virhe: T ei voi alkaa merkillä % int main() { T(); Ep(); printf("pop r1\n"); return 0; Edellisessä koodissa välike E on oleellisesti poistettu, ja se on korvattu sääntöjen oikealla puolella suoraan johdolla TE. Kielioppi generoi edelleen saman kielen: S TE E +TE TE ε T a (TE ) Lähtömuuttujasymboli S vastaa siis pääohjelmaa (main). 150

Konekielikäskymme: push x laita x pinoon pop x poista pinon päällimmäinen, Tulos ja laita x:ään add r1,r2r1 r1 + r2 sub r1,r2r1 r1 r2 on siis lopuksi rekisterissä r1. Generoitu konekieli ei tosin ole kovin tehokasta... Tätä ei kysytä tentissä! Se on esimerkkinä oikeasta jäsentämisestä ja kääntämisestä tosin ilman sellaisia käytännön kysymyksiä kuin jäsennysvirheiden käsittely, jne. Lelu-ohjelmamme tulostus syötteellä (x + y) (a + b) : push x push y pop r1 pop r2 add r1, r2 push r1 push a push b pop r1 pop r2 add r1, r2 push r1 pop r2 pop r1 sub r1, r2 push r1 pop r1 Peruuttavasta jäsentämisestä Voimme ryhtyä ohjelmoimaan tämän kaltaista rekursiivisesti etenevää jäsentäjää myös sellaiselle kieliopille G joka ei olekaan LL(1). Silloin tehdäänkin peruuttava (englanniksi backtracking ) jäsentäjä ennustavan sijaan. 1. Jäsentäjä arvaa (ennustamisen sijaan) mikä voisi olla seuraava produktio. 2. Jos jäsentäjä joutuu myöhemmin umpikujaan, eli huomaa arvanneensa väärin, niin se peruuttaa rekursiossaan viimeisimmän arvauksensa,... 3....ja arvaakin sen sijaan jonkin muun produktion. Intuitiivisesti, otamme aiemman kuvan 21 generoi-ja-testaa -algoritmin, ja toteutamme sen epädeterminismin tällä peruuttavalla etsinnällä. Tämän menetelmän hankaluuksia ovat edestakaisin vaeltelu syötemerkkijonossa: Jäsennin kulkee eteenpäin arvattuaan produktion jota se kokeilee seuraavaksi, ja taaksepäin peruuttaessaan vääräksi osoittautuneen arvauksensa. tehottomuus jos kieliopissa on paljon kokeiltavia vaihtoehtoja: Jäsennin joutuu kokeilemaan ne kaikki rekursiivisesti. pysähtyminen jos kielioppiin on jäänyt vasenta rekursiota: Jäsennin voi juuttua arvailemaan loputtomiin liikkumatta syötemerkkijonossa. Tällaisten peruuttavien etsintämenetelmien ohjelmointi yksinkertaistuu huomattavasti, jos otetaan käyttöön laiskat listat. 151

Laiskaa listaa ylläpidetään keskeneräisenä : Kun siltä kysytään Mikä on seuraava alkiosi? niin se laskee seuraavan alkionsa vasta silloin ja vain sen seuraavan alkionsa, eikä vielä muita. Peruuttavassa jäsennyksessä välikettä A vastaava jäsennysfunktio ottaa parametrinaan syötemerkkijonosta sen loppuosan u, joka on yhä jäsentämättä antaa tuloksenaan laiskan listan päätemerkkijonoja w, jossa w on se loppuosa merkkijonosta u, joka jää jäljelle kun sen alkuosasta jäsennetään tämä välike A. Siis u = vw jossa A v. Tuloslista koostuu kaikista tällaisista w, eli...... kaikista eri vaihtoehdoista jatkaa jäsennystä, kun ensin on jäsennetty tämä A. Silloin välikkeen A säännöistä A α 1... α k muodostetaan aliohjelma A(u) koodinaan 1 return Laiska(α 1, u)... Laiska(α k, u) jossa operaatio X Y yhdistää kaksi laiskaa listaa Y ja Y yhdeksi laiskaksi listaksi: 1 if lista X osoittautuu tyhjäksi 2 then return Y 3 else Z listan X ensimmäinen alkio; 4 L listan X loput alkiot; 5 return lista jonka ensimmäinen alkio on Z ja loput Y L. Laiskuuden ideana on laskea listaa X vain sen verran, että if-lausessa tiedetään kumpi haaroista then vaiko else pitää valita. Rivillä 5 kuljetaan listoja X ja Y vuorotahtiin; silloin jäsennys antaa reilun tilaisuuden jokaiselle eri kokeiltavalle vaihtoehdolle. Nämä Laiskat haarat voidaan puolestaan määritellä rekursiolla sääntöjen oikeiden puolten α i rakenteen suhteen: Laiska(ε, u) = return se laiska lista, jonka ainoa alkio on u itse koska tyhjän merkkijonon jäsentäminen ei kuluta yhtään syötemerkkiä. Päätemerkillä b Σ on Laiska(bβ, u) ehto 1 if merkkijono u on muotoa bw 2 then return Laiska(β, w) 3 else return tyhjä laiska lista koska tyhjä tuloslista tarkoittaa että ei jäsenny mitenkään. Välikkeellä B saa Laiska(Bβ,u) muodon 1 return Laiska(β, w 1 ) Laiska(β, w 2 ) Laiska(β, w 3 )... jossa w 1, w 2, w 3,... on laiskan listan B(u) sisältö 152

koska se tarkoittaa että jatketaan jäsentämällä β jokaisesta sellaisesta merkkijonosta w j joka jää jäljelle kun merkkijonon u alusta on jäsennetty välike B. Pääohjelmaksi alkusymbolille S tulee 1 return löytyykö ε laiskasta listasta S(koko syöte)? koska se tarkoittaa että voiko koko syötteen alusta jäsentää välikkeen S niin, ettei mitään jää jäljelle? Jos halutaan tämän kyllä/ei-vastauksen sijasta tuottaa jäsennyspuut, niin laajennetaan jokaisessa jäsennysfunktiossa A(u) jokainen tuloslistan alkio pelkästä merkkijonosta w pariksi (τ, w) jossa τ on sellainen jäsennyspuu, jonka juuri on nykyinen välike A ja tuotos on se merkkijono v jolla u = vw ja A v. Näin saa laiskan listan koko syötteen kaikista jäsennyspuista. Tällaista laiskoilla listoilla toteutettua peruttavaa jäsennintä voi onneksi tehostaa, jos kielioppisäännöt ovat sopivia. Esimerkiksi jos välikkeen säännöistä A α 1... α k tiedetään, että ne ovatkin muotoa joko α 1 tai α 2 tai α 3 tai...tai α k jos ne esimerkiksi täyttävät LL(1)- ehdot, vaikka koko kielioppi ei täytäkään niin silloin vastaavaksi aliohjelmaksi A(u) voidaankin ottaa 1 return Laiska(α 1, u)... Laiska(α k, u) jossa X Y sanookin että käytä listaa Y vain jos lista X osoittautuukin tyhjäksi : 1 if lista X osoittautuu tyhjäksi 2 then return Y 3 else return X. 5.6.2 LR-kieliopeista Simuloidaankin merkkijonon oikeaa johtoa rekursiivisesti. Saadaan LR(1)-kieliopit ja -kielet: Left to right scan, producing Right parse with 1 symbol lookahead. Yleisemmin, LR(k)-kielissä seuraavat k merkkiä määrittävät seuraavan johtoaskeleen. LR(0) = ns. yksinkertainen LR (Simple LR, SLR). LR(1) = deterministiset kielet, joten tasot k > 1 ovat enää teoreettisesti kiinnostavia. LR-jäsennys sisältää LL-jäsennyksen sillä lim LL(k) = LR(1). k Intutiivisesti, odotamme jäsentäessämme mahdollisimman pitkään emmekä heti kokeile sääntöä, eli teemmekin oikean emmekä vasenta johtoa. 153