Övningsexempel i Artificiella språk och syntaxanalys 2D1373

Relevanta dokument
2D1373 Artificiella språk och syntaxanalys

2D1372 Artificiella språk och syntaxanalys

Idag: Reguljära språk Beskrivs av Reguljära uttryck DFA Grammatik

Föreläsning 9: Turingmaskiner och oavgörbarhet. Turingmaskinen. Den maximalt förenklade modell för beräkning vi kommer använda är turingmaskinen.

DD1361 Programmeringsparadigm. Formella Språk & Syntaxanalys. Per Austrin

DD1361 Programmeringsparadigm. Formella Språk & Syntaxanalys. Per Austrin

Turingmaskiner och oavgörbarhet. Turingmaskinen. Den maximalt förenklade modell för beräkning vi kommer använda är turingmaskinen.

MÄLARDALENS HÖGSKOLA. CD5560 Formella språk, automater och beräkningsteori. Användarmanual. för simulatorn JFLAP

DAB760: Språk och logik

b) S Ø aa, A Ø aa» bb, B Ø aa» bc, C Ø ac» bc» 2. Låt L vara språket över 8a< som nedanstående NFA accepterar.

Föreläsning 7: Syntaxanalys

Inlämningsuppgift MiniPlotter

PROV I MATEMATIK Automatateori och formella språk DV1 4p

Automatateori (2) Idag: Sammanhangsfria språk. Dessa kan uttryckas med Grammatik PDA

Introduktion till formella metoder Programmeringsmetodik 1. Inledning

Kontextfria grammatiker

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

i=1 c i = B och c i = a i eller c i = b i för 1 i n. Beskriv och analysera en algoritm som löser detta problem med hjälp av dynamisk programmering.

Objektorienterad modellering och diskreta strukturer. 13. Problem. Sven Gestegård Robertz. Datavetenskap, LTH

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Föreläsning 9: NP-fullständighet

Alfabeten, strängar och språk. String

Parameteröverföring. Exempel. Exempel. Metodkropp

Definition. Mängden av reguljära uttryck på alfabetet Σ definieras av. om α och β är reguljära uttryck så är (α β) ett reguljärt uttryck

Klassdeklaration. Metoddeklaration. Parameteröverföring

Programmering A. Johan Eliasson

Grundläggande datalogi - Övning 9

DD1361 Programmeringsparadigm. Formella Språk & Syntaxanalys. Per Austrin

Reguljära uttryck Grammatiker Rekursiv nedåkning Allmänna kontextfria grammatiker. Syntaxanalys. Douglas Wikström KTH Stockholm

Användarhandledning Version 1.2

Dynamisk programmering

Övningsuppgifter till föreläsning 2 Variabler och uttryck

Algoritmer, datastrukturer och komplexitet

Datalogi I, grundkurs med Java 10p, 2D4112, Fiktiv tentamen, svar och lösningar och extra kommentarer till vissa uppgifter 1a) Dividera förs

Logik och kontrollstrukturer

Objektorienterad programmering i Java I. Uppgifter: 2 Beräknad tid: 5-8 timmar (OBS! Endast ett labbtillfälle) Att läsa: kapitel 5 6

Algoritmer, datastrukturer och komplexitet

Föreläsning 2 Programmeringsteknik och C DD1316. Mikael Djurfeldt

Föreläsning 7: Syntaxanalys

Pascal. reserverade ord fördefinierade funktioner och procedurer egendefinierade funktioner, procedurer och objekt

Algoritmer, datastrukturer och komplexitet

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

TDIU01 - Programmering i C++, grundkurs

Logik och Jämförelser. Styrsatser: Villkorssatsen if och repetitonssatsen for. Scriptfiler. Kommentarer. Tillämpningar: Ett enkelt filter.

MMA132: Laboration 2 Matriser i MATLAB

Pascal... Pascal. Pascal... Pascal...

Uppgift 1 ( Betyg 3 uppgift )

TDDC77 Objektorienterad Programmering

DD1361 Programmeringsparadigm. Carina Edlund

Lite mer psykologi. L2: Automater, Sökstrategier. Top-down. Kimballs sju principer

10. Mängder och språk

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

Komplexitetsklasser och repetition

Tentamen. Datalogi I, grundkurs med Java 10p, 2D4112, Lördagen den 30 november 2002 kl , salar E33, E34

Grammatik. BNF-grammatik

Tentamen, Algoritmer och datastrukturer

Föreläsning 7+8: NP-problem. Begreppet effektiv algoritm är alltså synonymt med går i polynomisk tid i den här kursen. Är detta en rimlig uppdelning?

Föreläsningsanteckningar, Introduktion till datavetenskap HT S4 Datastrukturer. Tobias Wrigstad

Övningsuppgifter kapitel 8

Universitetet i Linköping Institutionen för datavetenskap Anders Haraldsson

Föreläsning 3-4 Innehåll

Programmeringsteknik med C och Matlab

Uppgifter till praktiska tentan, del A. (7 / 27)

TENTAMEN TDDB53. Programmering i Ada för MI (provkod TEN2) den 7 april 2010 kl Institutionen för datavetenskap, IDA Olle Willén mars 2010

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret Lektion 4

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Uppgift 1 (grundläggande konstruktioner)

Objektorienterad Programmering (TDDC77)

Övningshäfte 2: Induktion och rekursion

Grunderna i stegkodsprogrammering

Bakgrund och motivation. Definition av algoritmer Beskrivningssätt Algoritmanalys. Algoritmer. Lars Larsson VT Lars Larsson Algoritmer 1

Lösning till tentamensskrivning i Diskret Matematik för CINTE, CL2 och Media 1, SF1610 och 5B1118, onsdagen den 17 augusti 2011, kl

TENTAMEN I PROGRAMSPRÅK -- DVG C kl. 08:15-13:15

Värmedistribution i plåt

Labb i Datorsystemteknik och programvaruteknik Programmering av kalkylator i Visual Basic

I Skapa Hej.java och skriv programmet. I Kompilera med javac Hej.java. I Rätta fel och repetera tills du lyckas kompilera ditt program

Övningshäfte 6: 2. Alla formler är inte oberoende av varandra. Försök att härleda ett par av de formler du fann ur några av de övriga.

SMD 134 Objektorienterad programmering

Tentamen i. TDDC67 Funktionell programmering och Lisp

Sanningsvärdet av ett sammansatt påstående (sats, utsaga) beror av bindeord och sanningsvärden för ingående påståenden.

Parsning. TDP007 Konstruktion av datorspråk Föreläsning 6. Peter Dalenius Institutionen för datavetenskap

Tentamen DE12, IMIT12, SYST12, ITEK11 (även öppen för övriga)

Datastrukturer och algoritmer. Föreläsning 15 Inför tentamen

Inlämningsuppgift : Finn. 2D1418 Språkteknologi. Christoffer Sabel E-post: csabel@kth.se 1

DD1350 Logik för dataloger. Fö 7 Predikatlogikens semantik

Johan Karlsson Datavetenskap för teknisk kemi, 10p, moment 1 Datavetenskap Umeå Universitet. Tentamen

Sats. Om t är en rätvinklig triangel så är summan av kvadraterna på kateterna i t lika med kvadraten på hypotenusan.

Exempeltenta GruDat 2002/2003

TENTAMEN: Algoritmer och datastrukturer. Läs detta!

Tentamen Datastrukturer, DAT037 (DAT036)

Backcode. Jonathan Crusoe TDP019 Projekt: Datorspråk Linköpings universitet

Övning 5 - Tillämpad datalogi 2013

Grundläggande logik och modellteori

Föreläsning 11 - Automater, textsökning, tillstånd

PROMETHEUS. Ett typat, objektorienterat programmeringsspråk av Tim Andersson.

Algoritmer, datastrukturer och komplexitet

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Introduktion till MATLAB

Linköpings Tekniska Högskola Instutitionen för Datavetenskap (IDA) Torbjörn Jonsson, Erik Nilsson Lab 2: Underprogram

Tentamen i Algoritmer & Datastrukturer i Java

Transkript:

Numerisk analys och datalogi Övningsexempel i Artificiella språk och syntaxanalys 2D1373 Lars Engebretsen Mikael Goldman Viggo Kann 12 februari 2002

2

Innehåll 0 Inledning 7 0.1 Ändliga automater......................... 7 0.2 KMP-automater........................... 8 0.3 Stackautomater........................... 9 0.4 Turingmaskinen........................... 11 I Applex 13 1 Simulering av NFA 15 1.1 Bakgrundskunskaper........................ 15 1.2 Datastrukturer i simulatorn..................... 15 1.3 Algoritmbeskrivning för simulatorn................ 16 1.4 Implementation........................... 18 1.5 Experiment............................. 18 2 Lexikal analys 19 2.1 Bakgrundskunskaper........................ 19 2.2 Implementation........................... 19 3 Rekursiv medåkning 21 3.1 Bakgrundskunskaper........................ 21 3.2 Implementation........................... 22 3.3 Om du har tid och lust....................... 22 4 Reguljära uttryck till NFA 25 4.1 Bakgrundskunskaper........................ 25 4.2 Implementation Steg 1: Syntaxanalysator............ 26 4.3 Implementation Steg 2: Översättare............... 26 II Övningsexempel 29 5 Ändliga automater 31 3

5.1 Att avgöra delbarhet........................ 31 5.2 KMP-automater........................... 32 5.3 Taldubbling............................. 32 5.4 Hur kan en DFA minnas saker?................... 32 5.5 Ickedeterminism.......................... 32 5.6 Delbarhet i ny tappning....................... 32 5.7 Stackautomater........................... 33 5.8 Att konstruera en turingmaskin................... 33 5.9 De olika automaternas förmåga................... 33 6 Reguljära uttryck 35 6.1 Reguljära uttryck för olika typer av tal............... 35 6.2 RE NFA DFA......................... 35 6.3 RE DFA............................. 35 6.4 DFA RE............................. 36 6.5 Baklängesspråk........................... 36 6.6 CNF-formler............................ 36 6.7 Översättning av å, ä och ö...................... 37 6.8 HTML-tabeller........................... 37 7 Grammatiker 39 7.1 Engelsk minigrammatik....................... 39 7.2 Funktionsanrop........................... 40 7.3 Reguljära uttryck.......................... 40 7.4 EBNF och BNF........................... 40 7.5 Matrismultiplikation........................ 40 7.6 Inte palindrom............................ 41 7.7 Vektoruttryck............................ 41 7.8 Kontextfri grammatik NPDA.................. 42 7.9 Syntaxanalys av kontextfria grammatiker............. 42 8 LL-analys 43 8.1 Vänsterrekursion.......................... 43 8.2 First-mängder............................ 43 8.3 Taluttryck.............................. 43 8.4 Rekursiv medåkning........................ 44 8.5 Nästlade kommentarer....................... 44 8.6 Binära heltal............................. 45 9 LR-analys 47 9.1 LR-tabellen............................. 47 9.2 LR(0)-konstruktion......................... 48 9.3 SLR(1), men inte LR(0)...................... 48 9.4 LALR(1), men inte SLR(1)..................... 48 4

9.5 LR(1), men inte LALR(1)..................... 49 9.6 Räknedosa med variabler...................... 49 9.7 Räknedosa med funktionsanrop................... 49 9.8 Textkomprimering......................... 50 9.9 Lista i lista.............................. 50 III Ledningar 53 IV Lösningar 59 5

6

Kapitel 0 Inledning Detta häfte innehåller övningsexempel till kursen Artificiella språk och syntaxanalys. Vi har strävat efter att ta med utförliga lösningar, så att det ska vara möjligt att bedriva självstudier med detta kompendium och kursboken som stöd. Kursboken är för närvarande Parsons [9]. Det går också att använda den så kallade drakboken [1]. Ett tredje alternativ är att använda någon av Appels böcker [2, 3, 4] och kombinera den med någon bok som behandlar formella språk. Det finns flera sådana böcker, en klassiker är Hopcroft och Ullman [6]. Den finns också i en nyare variant [7]. En kompakt skriven, men mycket prisvärd bok är Révész [10]. I kursen behandlas ett antal olika automatmodeller. Vi kommer nedan att kortfattat beskriva dessa modeller, så att de beteckningar som används i kursen finns samlade på ett ställe. 0.1 Ändliga automater DFA:er och NFA:er finns utförligt beskrivna i kursboken [9], under benämningen FSA. För fullständighets skull skriver vi ner den formella definitionen här. Definition 1. En DFA (deterministisk ändlig automat, deterministic finite automaton) är en femtupel (Σ, Q, q 0 Q, F Q, δ : Q Σ Q). Σ är alfabetet (en ändlig mängd symboler), Q är tillståndsmängden (även den är ändlig), q 0 är starttillståndet, F mängden av accepterande tillstånd och δ är övergångsfunktionen. Övergångsfunktionen anger, för varje tillstånd i Q, vilket tillstånd automaten ska gå till, givet en viss indatasymbol. Automaten accepterar om den befinner sig i ett accepterande tillstånd då all indata lästs. Definition 2. En NFA (ickedeterministisk ändlig automat, nondeterministic finite automaton) definieras som en DFA, förutom att övergångsfunktionen blir en övergångsrelation, δ Q (Σ {ε}) Q. En NFA kan följa en övergång utan att läsa 7

någon indatasymbol. Dessa övergångar betecknas med symbolen ε, som alltså inte ingår i alfabetet. När en NFA befinner sig i ett visst tillstånd kan det alltså finnas flera möjliga övergångar att välja mellan. Automaten accepterar en sträng om det finns något sätt att välja övergångar så att automaten befinner sig i ett accepterande tillstånd när strängen lästs. För att kunna analysera hur kraftfull en viss automatmodell är tittar vi på de språk som automatmodellen klarar av att känna igen. Det verkar rimligt att säga att en automatmodell som kan användas för att känna igen en stor mängd språk är mer kraftfull en automatmodell som bara kan känna igen några få språk. Definition 3. Klassen DFA är mängden av alla språk som känns igen av någon DFA. Klassen NFA är mängden av alla språk som känns igen av någon NFA. Klassen RE är mängden av alla språk som kan beskrivas av något reguljärt uttryck. Sats 1. DFA = NFA, dvs för varje språk som känns igen av någon NFA finns det en DFA som avgör samma språk. Bevis. Eftersom varje DFA även är en NFA kan NFA:er känna igen alla språk som någon DFA kan känna igen. Dessutom visas i kursbokens [9] avsnitt 2.5 att man för varje NFA kan konstruera en DFA som simulerar den. Detta fullbordar beviset. Sats 2. RE = DFA, dvs reguljära uttryck kan generera precis de språk som känns igen av någon DFA. Bevis. Se exempel 6.3 och exempel 6.4. Ofta konstrueras automaterna grafiskt. Då betyder figuren q a 0 q 1 att automaten ska gå till tillstånd q 1 om den befinner sig i tillstånd q 0 och nästa indatasymbol är a. En ickedeterministisk automat kan ha flera möjliga övergångar att följa, den väljer då en av dem. Starttillståndet betecknas av att det finns en fri övergångspil som leder in till det, och accepterande tillstånd betecknas med dubbla ringar. (Detta gäller alla typer av automater som behandlas i kursen.) Nedan är q 0 starttillstånd och q 1 ett accepterande tillstånd. q 0 q 1 0.2 KMP-automater KMP-automater (KMP står för Knuth, Morris och Pratt) är en tillämpning av ändliga automater. En KMP-automat är en DFA som har egenskapen att den accepterar 8

så snart ett accepterande tillstånd nås. Genom att använda en KMP-automat vid strängsökning kan man ibland utnyttja att man redan har hittat en delsträng i den sträng man söker efter. För mer information, se exempel 5.2 i detta häfte eller originalartikeln av Knuth, Morris och Pratt [8]. 0.3 Stackautomater I kursboken [9] visas att det finns språk som inte är reguljära, dvs språk som inte kan kännas igen av någon DFA. Ett exempel på ett sådant språk är L = {1 n 0 m 1 n : n, m > 0}. (1) Detta språk kan kännas igen av en stackautomat. En stackautomat fungerar som en ändlig automat utökad med en stack. Först definierar vi en ickedeterministisk stackautomat, NPDA, och därefter en deterministisk stackautomat, DPDA. En ickedeterministisk stackautomat, NPDA, fungerar som en NFA utökad med en stack. Den läser indata på samma sätt som en ändlig automat, men övergångarna beror nu inte bara på nästa indatasymbol, utan även på den symbol som ligger överst på stacken. Dessutom har NPDA:n möjlighet att lägga symboler på stacken under övergångarna. Definition 4. En NPDA (ickedeterministisk stackautomat, nondeterministic pushdown automaton) är en sextupel (Σ, Γ, Q, q 0, F, δ), där 1. Σ är indataalfabetet (en ändlig mängd symboler), 2. Γ är stackalfabetet (också en ändlig mängd symboler), 3. Q är tillståndsmängden (även den är ändlig), 4. q 0 Q är starttillståndet, 5. F Q mängden av accepterande tillstånd och 6. δ Q (Σ {ε}) Γ Q Γ är övergångsrelationen. Övergångsrelationen anger, för varje tillstånd i Q, vilka tillstånd automaten kan gå till och vilka symboler den kan skriva på stacken, givet en viss inläst indatasymbol och en viss symbol som poppats från stacken. Det är vanligt att Σ Γ, men det är inte nödvändigt. Om (p, a, b, q, cab) δ betyder det att om NPDA:n är i tillstånd p, läser a och poppar b kan den gå till tillstånd q och lägga cab på stacken (symbolerna läggs ovanpå varandra med sista symbolen, b, överst). Om andra komponenten är ε betyder det att NPDA:n inte läser någon indatasymbol, utan bara tittar på aktuellt tillstånd och poppad symbol. Om femte komponenten är ε betyder det att NPDA:n inte lägger några symboler på stacken i detta steg. Det är ganska vanligt att en övergång (p, x, y, q, σ) δ skrivs (p, x, y) (q, σ). 9

En inmatning accepteras av en NPDA om det finns någon väg genom automaten som leder till ett accepterande tillstånd. Att automaten alltid måste läsa en symbol från stacken utgör ingen begränsning, den kan skriva tillbaka samma symbol till stacken igen under övergången. Definition 5. En DPDA (deterministisk stackautomat, deterministic pushdown automaton) definieras precis som en NPDA, men med restriktioner på δ: 1. Om DPDA:n har en övergång (p, x, y) (q, σ) har den ingen övergång (p, x, y) (q, σ ) där q q eller σ σ. 2. Om DPDA:n har en övergång (p, ε, y) (q, σ) har den ingen övergång (p, ε, y) (q, σ ) där q q eller σ σ. 3. Om DPDA:n har en övergång (p, ε, y) (q, σ) har den ingen övergång (p, x, y) (q, σ ) där x Σ. Detta betyder att givet aktuellt tillstånd p och stacktopp b vet man säkert om man ska läsa en indatasymbol eller ej, och därefter finns bara ett sätt att välja nästa tillstånd och symboler att lägga på stacken. Det är lättast att beskriva en NPDA (eller DPDA) grafiskt. Nedan visar vi hur övergången (q 0, a, b) (q 1, c 1 c 2 c 3 ) betecknas. a/b, c q 1 c 2 c 3 0 q 1 Denna övergång innebär: Om automaten befinner sig i tillstånd q 0, nästa indatasymbol är a och symbolen överst på stacken är b, ska den läsa a och b, gå till tillstånd q 1 och lägga c 1, c 2 och c 3 på stacken, i tur och ordning. (Alltså ligger c 3 överst på stacken efter övergången.) Som ytterligare exempel visar vi en DPDA som känner igen språket L från ekvation 1. 1/1, 11 0/1, 1 1/1, ε 1/#, #1 0/1, 1 1/1, ε ε/#, # q 0 q 1 q 2 q 3 q 4 Definition 6. Klassen DPDA är mängden av alla språk som känns igen av någon DPDA. Klassen NPDA är mängden av alla språk som känns igen av någon NP- DA. Klassen CFL är mängden av alla språk som genereras av någon kontextfri grammatik. Sats 3. DFA DPDA, dvs varje språk som känns igen av någon DFA kan även kännas igen av någon DPDA, men det finns språk som kan kännas igen av en DPDA som inte kan kännas igen av någon DFA. 10

Bevis. Eftersom varje DFA även är en DPDA som inte använder stacken, är DPDA minst lika kraftfull som DFA. Ett exempel på ett språk L som inte känns igen av någon DFA gav vi i ekvation 1. Vi ger också två satser om NPDA:er utan stringenta bevis. Sats 4. DPDA NPDA. Ofullständigt bevis. Språket av palindromer kan kännas igen av en NPDA. Att det inte kan kännas igen av någon DPDA är sant, men visas inte här. Sats 5. NPDA = CFL. Ofullständigt bevis. Om ett språk S kan beskrivas med en kontextfri grammatik finns det en NPDA som känner igen S; NPDA:n konstrueras som i exempel 7.8. Att NPDA CFL är sant, men visas inte här. 0.4 Turingmaskinen En turingmaskin är en automat som har tillgång till ett obegränsat långt band som minne. På bandet, som från början innehåller indata, kan maskinen skriva och läsa symboler med ett läs- och skrivhuvud. Detta huvud kan flyttas ett steg vid varje övergång. Automaten börjar alltid i starttillståndet, och då står läs- och skrivhuvudet på första symbolen i indata. Indata omges av blanka, som vi betecknar med #. Om turingmaskinen hamnar i ett accepterande tillstånd avbryts beräkningen och Ja returneras, om maskinen hamnar i ett läge där det inte finns någon matchande övergång avbryts beräkningen och Nej returneras. Övergången a/b, L q 0 q 1 betyder: Om läshuvudet läser a, skriv b och flytta sedan huvudet ett steg åt vänster. Istället för L kan bokstäverna R respektive S anges; de betyder flytta huvudet ett steg åt höger respektive låt huvudet stå kvar. En ickedeterministisk turingmaskin kan från samma tillstånd ha flera övergångar med samma lässymbol. Vid en sådan övergång gör maskinen ett ickedeterministiskt val. Om det finns någon följd av val som leder fram till ett accepterande tillstånd accepterar maskinen, annars accepterar den inte. Varje ickedeterministisk turingmaskin går att simulera med en deterministisk turingmaskin. I analogi med tidigare automatmodeller definierar vi språkklasser som hör till turingmaskinen. Avslutningsvis ger vi, utan strikt bevis, två satser angående turingmaskiners kraftfullhet. Definition 7. Klassen TM är mängden av alla språk som känns igen av någon deterministisk turingmaskin. Klassen NTM är mängden av alla språk som känns igen av någon ickedeterministisk turingmaskin. 11

Sats 6. NPDA TM = NTM. Ofullständigt bevis. Språket {0 n 1 n 0 n, n > 0} kan kännas igen av en turingmaskin. Att det inte kan kännas igen av någon NPDA visar vi inte här. Varje ickedeterministisk turingmaskin kan simuleras med en deterministisk med en metod liknande den som används för att simulera en NFA med en DFA. Sats 7. TM är exakt lika med mängden av alla språk som kan beskrivas av en generell frasstrukturgrammatik. 12

Del I Applex 13

Kapitel 1 Simulering av NFA Detta exempel syftar till att du ska förstå hur en icke-deterministisk ändlig automat (NFA) fungerar. Du kommer att skriva ett program som simulerar en NFA deterministiskt och dessutom studera hur simuleringen går till. Du kommer att behöva detta program för applex 4 i kapitel 4. De filer du behöver använda för denna uppgift finns i katalogen /info/syntax02/applex/1 i Nadas unix-system. 1.1 Bakgrundskunskaper Vad ändliga automater är beskrivs i inledningen till detta häfte och i avsnitten 2.3 och 2.4 i kursboken [9]. Läs dessa avsnitt. Vi använder samma beteckningar här som i häftets inledning. Vid simuleringen ställer vi för enkelhets skull några extra krav på automaten. 1. Tillstånden är numrerade med naturliga tal där tillstånd nummer noll är starttillståndet. 2. Från varje tillstånd får det antingen gå ingen övergång alls, en teckenövergång, en epsilonövergång eller två stycken epsilonövergångar (det vill säga en ickedeterministisk förgrening). Detta är ingen svår begränsning alla ickedeterministiska ändliga automater kan enkelt skrivas om på ett sånt sätt. 3. När (och om) automaten når ett accepterande tillstånd accepterar den, även om det finns inmatning kvar. Kontrollera dig själv: Fundera igenom hur en godtycklig NFA kan skrivas om så att den passar algoritmen i denna laboration. 1.2 Datastrukturer i simulatorn Övergångsrelationen δ ska realiseras med en vektor vars element har följande typ: 15

/* C */ // Java typedef struct transition_ class NFATransition { { char symbol; char symbol; int left, right; int left, right; } transition_t; } Fältet symbol anger vilket tecken som läses vid övergången. Vi använder # för att markera att det är en epsilonövergång. Om nu s är en sådan vektor lagras (q 8, A, q 17 ) δ genom att s[8] = { A, 17, -1} och (q 5, ε, q 2 ) δ lagras som s[5] = { #, 2, -1} Om det skulle ha funnits två epsilonövergångar från tillstånd q 5 skulle s[5].right ha använts för att lagra den andra. Automaten q A 5 q 6 ε q ε 0 q 4 q 1 ε ε A q 2 q ε 3 lagras på följande sätt: C B q ε 7 q D 8 q ε 9 q 10 i 0 1 2 3 4 5 6 7 8 9 10 s[i].symbol # A # B # A C # D # # s[i].left 4 2 3 7 5 6 7 8 9 10-1 s[i].right -1-1 1-1 2-1 -1-1 -1-1 -1 1.3 Algoritmbeskrivning för simulatorn Algoritmen vi kommer att använda är exakt den som beskrivs i kursbokens [9] avsnitt 2.5, The Subset Construction. Vi simulerar den givna NFA:n genom att, vid varje position i indatasträngen, komma ihåg alla tillstånd som NFA:n kan befinna sig i vid denna position. Vi kommer alltså, i varje steg av NFA:ns simulering, att hålla reda på en delmängd av tillståndsmängden. (Nämligen den delmängd som består av de tillstånd NFA:n kan befinna sig i just då.) Givet denna delmängd, övergångsrelationen och nästa indatasymbol ska vi kunna konstruera en ny delmängd: Mängden av de tillstånd NFA:n kan befinna sig i när den läst indatasymbolen. Om vi låter NFA:n ha tillstånden q 0,..., q n 1, F vara mängden av accepterande tillstånd och s vara vektorn med övergångar får vi följande algoritm: M {q 0 } M EpsilonClosure(M) 16

if F M then return True repeat c NextSymbol() M SymbolTransition(M, c) M EpsilonClosure(M) if F M then return True until M = or NoMoreSymbols() return False Ovan är NextSymbol() en funktion som returnerar nästa indatasymbol. NoMore- Symbols() är en funktion som returnerar sant om det inte finns några fler indatasymboler. Funktionen SymbolTransition(M, c) ska givet en tillståndsmängd M och en indatasymbol c returnera en mängd som innehåller de symboler som kan nås om man från varje tillstånd i M följer de övergångar som är märkta med c. Funktionen kan beskrivas av följande algoritm: M for each q i M do if s[i].symbol = c M M {q s[i].left } return M Nu återstår bara funktionen EpsilonClosure(M), som givet en tillståndsmängd M skall returnera mängden av de tillstånd man kan nå genom att följa ett godtyckligt antal ε-övergångar från något av tillstånden i M: repeat for each q i M do if s[i].symbol = # if s[i].left 1 M M {q s[i].left } if s[i].right 1 M M {q s[i].right } until M does not change anymore return M För att kunna implementera algoritmerna ovan måste vi kunna representera mängderna F, M och M. Ett enkelt sätt att göra detta är att låta dem representeras av var sin heltalsvektor med n element. En nolla i position i innebär att q i inte tillhör mängden, en etta att motsvarande element tillhör mängden. Ett snyggare och datalogiskt mer professionellt sätt är att skapa en abstrakt datatyp för mängder av heltal som alternativ till att skriva en helt egen kan kanske klassen std::set<> i STL-biblioteket till C++ eller java.lang.set användas som utgångspunkt och implementera de metoder som krävs för att lösa uppgiften. Kontrollera dig själv: Se till att du förstår de givna algoritmerna. 17

1.4 Implementation Skriv en funktion simulate() som simulerar en automat enligt beskrivningen ovan. Gränssnittet finns specificerat i filerna c/nfasim.h respektive java/nfa. java i uppgiftskatalogen. Det finns ett färdigt huvudprogram som läser in en automat från fil, och sedan läser in text radvis och provkör simuleringsproceduren på texten. Använd detta huvudprogram för att provköra din funktion. Automaten i exemplet ovan finns i filen labb1.exempel. Testa exempelautomaten med några inmatningar. Vilket reguljärt uttryck motsvarar det språk exempelauto- Kontrollera dig själv: maten accepterar? 1.5 Experiment Lägg in en rad som skriver ut innehållet i M under simuleringen och kör sedan simulatorn på några olika indata. Jämför med bilden ovan av automaten. På detta sätt kan man få en känsla för hur en ickedeterministisk automat uppför sig. Kontrollera dig själv: De automater som behandlas i kursen fungerar normalt lite annorlunda än automaterna i denna laboration: Det normala är att automaten läser hela indata, och accepterar om den står i ett accepterande tillstånd när indata är slut. Fundera igenom hur algoritmerna ovan ska ändras för att man ska få det normala beteendet hos automaterna som simuleras. 18

Kapitel 2 Lexikal analys Detta exempel syftar till att du ska förstå det första steget i översättaren den lexikala analysatorn. Du kommer att använda (J)Flex för att skriva en lexikal analysator till en enkel kalkylator. Du kommer att behöva detta program till applex 3 i kapitel 3 nedan. De filer du behöver använda för denna uppgift finns i katalogen /info/syntax02/applex/2 i Nadas unix-system. 2.1 Bakgrundskunskaper Den lexikala analysen överätter en ström av tecken till en ström av slutsymboler. Dessa slutsymboler har ibland ett så kallat semantiskt värde. Du ska i denna laboration använda verktyget Flex om du programmer i C och JFlex om du programmerar i Java se till att ta del av dokumentationen till det verktyg du ska använda. I kurskatalogen finns en skelettfil för att hjälpa dig att komma igång. Dessutom finns det ett drivprogram som kan användas för testkörning. 2.2 Implementation Skriv en lexikalanalysator som känner igen följande slutsymboler: tecknen +, -, *, /, ^, (, ), = och \n. De slutsymboler som ska returneras finns i filerna c/symbol.h respektive java/symbol.java. Tänk på att många av tecknen ovan har speciell betydelse i (J)Flex och därför måste skyddas med ett bakvänt snedstreck eller citat-tecken. Du ska inte använda yylval eller den andra parametern till Symbol-objektets konstruktor än. Prova att kompilera och köra programmet. Titta på drivprogrammet och se till att du förstår hur den lexikala analysatorn används. Hur anropas den? Vad returnerar den? Ta också gärna en titt på den fil som genereras av (J)Flex. Bygg sedan ut analysatorn så att den känner igen även icke-negativa decimaltal (utan plus eller minus framför), nyckelordet LET och variabelnamn. Variabelnamn består av en sträng av bokstäver förutom strängen LET förstås. Returnera det 19

lexikala värdet med hjälp av yylval respektive Symbol-objektet. När du kör programmet ska rätt semantiskt värde skrivas ut precis efter respektive slutsymbol. Använder du C ska de semantiska värdena för tal respektive variabler ha typen double respektive char * tänk också på att de strängar som char-pekarna pekar på måste vara giltiga under hela programmets körning. Använder du Java ska de semantiska värdena för tal respektive variabler ha typen Double respektive String. Titta på drivprogrammet igen. Lägg märke till hur det lexikala värdet hanteras. Eftersom en funktion bara kan ha ett returvärde i C används den globala variabeln yylval för att skicka tillbaka det lexikala värdet till drivprogrammet. I Java är det lexikala värdet istället en del av det Symbol-objekt som returneras av analysatorn. Kontrollera dig själv: Vad är det egentligen programmet kontrollerar? Kan man skriva in felaktiga matematiska uttryck, som 3+/5 eller LET korv + mos = gott utan att programmet klagar? Varför beter sig programmet på det viset? 20

Kapitel 3 Rekursiv medåkning Detta exempel syftar till att du ska förstå hur rekursiv medåkning fungerar. Du kommer att bygga ut ett färdigt program som implementerar en enkel kalkylator. De filer du behöver använda för denna uppgift finns i katalogen /info/syntax02/ applex/3 i Nadas unix-system. Dessutom behöver du din lösning till applex 2. 3.1 Bakgrundskunskaper Rekursiv medåkning (recursive descent) är en metod att direkt från en grammatik skriva en enkel syntaxanalysator i ett vanligt programspråk, se avsnitt 3.3 3.4 i kursboken [9]. Din uppgift är att utöka en färdig analysator med ett antal regler. Den färdiga analysatorn är skriven i samma anda som analysatorn i kursbokens avsnitt 3.4.1 med ett undantag: Eftersom indata läses från en ström är det lite bökigt att trycka tillbaka slutsymboler på inströmmen. Därför finns det en färdig modul som tillåter att man tjuvtittar på nästa slutsymbol i inströmmen innan man läser in den. Du ska utgå från filen c/casio.c alternativt java/casio.java som innehåller en räknedosa med de fem räknesätten addition, subtraktion, multiplikation, division och exponentiering enligt nedanstående BNF-grammatik där vänsterrekursion tagits bort och alla regler vänsterfaktoriserats. <start> ::= NEWLINE <start> <expr> NEWLINE <start> ε <expr> ::= <term> <expr1> <expr1> ::= PLUS <term> <expr1> MINUS <term> <expr1> ε <term> ::= <factor> <term1> 21

<term1> ::= TIMES <factor> <term1> DIVIDE <factor> <term1> ε <factor>::= <prim> <factor1> <factor1>::= POWER <factor> ε <prim> ::= NUMBER MINUS <prim> LEFTPAREN <expr> RIGHTPAREN Som lexikal analysator ska du använda din lösning till applex 2 i kapitel 2. Kontrollera dig själv: Se till att du förstår den givna analysatorn i detalj. Varför behöver expr1() och term1() en parameter, när varken factor() eller prim() har någon? Enligt vilken regel har funktionernas returvärden valts? Hur ser Firstoch Follow-mängderna ut för de olika reglerna i grammatiken? Stämmer det med vad som finns i det givna programmet? 3.2 Implementation Bygg ut analysatorn med variabler och en tilldelningssats enligt följande tillägg till grammatiken: <start> ::= <assign> NEWLINE <start> <assign> ::= LET <variable> EQUALS <expr> <prim> ::= <variable> <variable> ::= VARIABLE Du måste också hålla reda på variablernas värden i en symboltabell med hjälp av någon lämpligt vald datastruktur. Kontrollera dig själv: ha? Vilket returvärde ska funktionen variable() lämpligen 3.3 Om du har tid och lust Det kan vara lite enklare att implementera följande EBNF-grammatik istället: 22

<start> <assign> ::= NEWLINE <start> <expr> NEWLINE <start> <assign> NEWLINE <start> ε ::= LET <variable> EQUALS <expr> <expr> ::= <term> { ( PLUS MINUS ) <term> } <term> ::= <factor> { ( TIMES DIVIDE ) <factor> } <factor> ::= <prim> [ POWER <factor> ] <prim> ::= NUMBER MINUS <prim> LEFTPAREN <expr> RIGHTPAREN <variable> <variable> ::= VARIABLE Fundera igenom vad som behöver ändras i det givna programmet och genomför ändringarna. Hur implementerar man lämpligen de åtgärder som hör till <expr> i en enda funktion? 23

24

Kapitel 4 Reguljära uttryck till NFA Detta exempel är en uppvärmning till projektuppgiften i kursen. Syftet är att du ska arbeta med verktygen Flex och Bison, alternativt JFlex och Cup, och skapa en översättare som syntaxanalyserar ett reguljärt uttryck och skriver ut en beskrivning av en ickedeterministisk ändlig automat som känner igen språket som det reguljära uttrycket beskriver. När programmet är klart ska man kunna köra det och skriva in ett reguljärt uttryck, till exempel x(uv w)* som motsvarar x(uv w) med kursbokens [9] notation. Programmet ska då på fil beskriva en ickedeterministisk ändlig automat som accepterar detta språk. Därefter ska man kunna använda programmet från applex 1 i kapitel 1 för att simulera automaten och testa om olika inmatningssträngar accepteras. 4.1 Bakgrundskunskaper Reguljära uttryck beskrivs i avsnitt 2.6 i kursboken [9]. Grammatiken för reguljära uttryck kan i BNF skrivas på följande sätt. <expression>::= <term> <expression> <term> <term>::= <factor> <term><factor> <factor>::= ( <expression> ) <factor> * v Lexikala regler: v betyder en bokstav (A Z eller a z); blanka får förekomma var som helst i uttrycket. Grammatiken ovan har medvetet skrivits vänsterrekursiv eftersom Bison och Cup är LALR-analysatorer, se sida 134 i kursboken [9] och avsnittet Recursion i kapitlet Bison Grammar Files i Bisonmanualen. Hur ett reguljärt uttryck översätts till en NFA beskrivs i avsnitt 2.7 i kursboken [9] samt i uppgift 6.2 i detta häfte. Det är lite bökigt att få Flex och Bison att arbeta ihop som man vill, i synnerhet som man i både Flex- och Bison-filen behöver veta vilka slutsymboler som används. För att göra det lättare att komma igång finns det skelettfiler i katalogen /info/syntax02/applex/4/c i Nadas unix-system. De innehåller deklarationer 25

som gör att Flex och Bison samarbetar på ett smidigt sätt. I dessa filer finns också några kodsnuttar för att hjälpa dig på traven. Notera att några av raderna ska tas bort när du gör den första implementationsuppgiften. Det är lite lättare att få JFlex och Cup att arbeta ihop, men även här är det lätt att fastna. Skelettfiler finns i katalogen /info/syntax02/applex/4/java. I dessa filer finns också några kodsnuttar för att hjälpa dig på traven. Notera att några av raderna ska tas bort när du gör den första implementationsuppgiften. 4.2 Implementation Steg 1: Syntaxanalysator Skriv om de kontextfria grammatiska reglerna ovan i Bison. Inför en extra regel < regexp > ::= < expression > och låt regexp vara startsymbol. Som lexikal analysator ska Flex eller JFlex användas. Låt LETTER vara slutsymbolen som motsvarar en bokstav. Låt huvudprogrammet bara kolla värdet av yyparse() respektive parse()- metoden och skriva korrekt (om det var ett riktigt reguljärt uttryck) eller felaktigt. Programmet är nu en färdig syntaxanalysator (parser) som känner igen språket av (korrekta) reguljära uttryck. Bygg programmet, kör det, och prova att mata in några riktiga och felaktiga reguljära uttryck. För att tala om att ett uttryck är färdiginmatat måste man kanske (beroende på hur din lexikala analysator ser ut) trycka på CTRL-d, dvs filslut, först på en rad. 4.3 Implementation Steg 2: Översättare Nu ska syntaxanalysatorn byggas ut till en översättare, som givet ett reguljärt uttryck skapar och matar ut en ickedeterministisk automat. Automaten representeras av vektorn statedata som består av tillstånd och övergångar. Varje övergång representeras av fyra fält: det tecken som finns på övergångarna ut från tillståndet, vart övergångarna går och en flagga som talar om ifall tillståndet är accepterande. Det finns färdiga rutiner för att skapa nya tillstånd, detaljerna finns i filerna nfa.h respektive NFA.java. Varje regel i din grammatik känner igen ett reguljärt uttryck som är ett deluttryck i hela det reguljära uttrycket. Låt regeln skapa den delautomat som motsvarar det deluttrycket. Denna delautomat har ett starttillstånd och ett sluttillstånd. För att delautomaten ska kunna kopplas ihop med resten av automaten måste dess starttillstånd och sluttillstånd bli kända för de andra reglerna. Detta går att göra genom att regeln som semantiskt värde returnerar en post som innehåller de två heltalen enter och exit som anger numren på delautomatens starttillstånd och sluttillstånd. Alltså måste varje regel sätta värden på $$.enter och $$.exit om du använder C respektive RESULT.enter och RESULT.exit om du använder Java. 26

När man skapar en delautomats sluttillstånd vet man inte ännu vart uthoppet ska gå, utan övergången måste sättas av den regel som senare kombinerar den aktuella delautomaten med nästa delautomat. Enklast blir det om du alltid ser till att uthoppet från delautomaten går från fältet left. Uthoppsövergångarena initieras automatiskt till 1, så är det lätt att se på automaten om något uthopp förblir otilldelat. Figuren nedan visar för varje regel hur motsvarande ändliga automat kan byggas. Alla övergångar är epsilonövergångar utom den som läser en bokstav i den nedersta automaten. Regeln för term factor skiljer sig från den i boken, och är lättare att implementera. expression term expression term term factor term factor factor * factor v v När vektorerna är färdiguppbyggda återstår bara att skriva ut dem på fil. När programmet är klart ska det kunna producera filer som kan läsas av simulatorn från laboration 1. Titta i de givna källkodsfilerna från laboration 1 för att ta reda på indatafilens format. Prova några reguljära uttryck och kolla att allt fungerar. Det är inte nödvändigt med någon felhantering. Kontrollera dig själv: Du ska inte använda den interna Bison-variabeln yylval i Bisonfilen. Den är bortabstraherad använd de olika $-variablerna istället. 27

28

Del II Övningsexempel 29

Kapitel 5 Ändliga automater Detta avsnitt innehåller exempel som är relaterade till olika typer av ändliga automater. I kursboken [9] behandlas i huvudsak deterministiska och ickedeterministiska ändliga automater. Dessa förkortas ofta DFA respektive NFA. (Boken använder ibland den mindre lyckade förkortningen FSA.) Vi kommer dessutom att behandla ändliga stackautomater. Dessa fungerar som en ändlig automat fast de har dessutom möjlighet att lagra och hämta värden från en stack. Man kan konstruera såväl deterministiska som ickedeterministiska stackautomater, de förkortas ofta DPDA respektive NPDA. Ytterligare en automatmodell är turingmaskinen, som förkortas TM. Den har möjlighet att läsa och skriva från ett band, och den kan flytta läsoch skrivhuvudet fram och tillbaka på bandet. Det verkar naturligt att en turingmaskin är åtminstone lika kraftfull som en stackautomat, eftersom turingmaskinen kan läsa och skriva var som helst i minnet medan stackautomaten bara kommer åt det översta elementet på stacken. Vidare verkar det naturligt stackautomaterna är minst lika kraftfulla som automaterna utan stack. I själva verket finns det en strikt hierarki mellan automattyperna, vilket man brukar skriva DFA = NFA DPDA NPDA TM = NTM. (5.1) Detta innebär att för varje nivå i hierarkin finns det ett språk som går att avgöra med en automat från den nivån, men som inte går att lösa med någon automat från den närmast lägre nivån. 5.1 Att avgöra delbarhet Konstruera en deterministisk ändlig automat (DFA) som känner igen språket av alla binära tal som inte är delbara med tre. Matematiskt kan vi skriva detta språk som L = {w {0, 1} : w mod 3 0}. (5.2) (Automaten ska alltså kunna svara på frågan: Tillhör den binära strängen x mängden L?.) Automaten äter siffrorna i strängen från vänster till höger. 31

5.2 KMP-automater Konstruera en KMP-automat som känner igen texten OOAOOD. (En KMP-automat är en minimal DFA som accepterar inmatningen så snart något accepterande tillstånd nås.) 5.3 Taldubbling (svår) I den här uppgiften ska vi konstruera en variant av en DFA, nämligen en DFA där varje övergång är märkt både med en inläst symbol och en utmatningssträng. Utmatningssträngen kan vara ε, den tomma strängen. Denna DFA ska läsa in ett decimalt, positivt heltal avslutat med punkt från vänster till höger och skriver ut dubbla talet. Rita inte automaten. Exempel: 41. 82, 47. 94, 8. 16. 5.4 Hur kan en DFA minnas saker? (ganska svår) Låt L m vara språket som består av alla binära strängar som har en etta i den m:te sista positionen, där m är ett positivt heltal. Ett sätt att skriva detta på är: L m = {w {0, 1} : w w m+1 = 1}. (5.3) (Ovan betecknar w längen av strängen w och w i det i:te tecknet i strängen w.) Konstruera en DFA som känner igen L m. 5.5 Ickedeterminism (ganska svår) Lös problemet i avsnitt 5.4 med en NFA istället. 5.6 Delbarhet i ny tappning (svår) Låt S vara språket av alla positiva heltal som är jämnt delbara med elva. Anta att talen skrivs binärt. Att konstruera en DFA som känner igen S då talen läses från vänster till höger kan göras på samma sätt som i avsnitt 5.1: Låt automaten bestå av 11 tillstånd numrerade 0, 1,..., 10 och konstruera den så att automaten befinner sig i tillstånd q i om det hittills inlästa talet modulo 11 är i. Då måste tillstånd nummer 0 vara både starttillstånd och accepterande tillstånd och övergångsfunktionen ges av δ(i, σ) = 2i + σ mod 11. (Tänk igenom detta.) Konstruera nu istället en DFA som känner igen S då talen läses från höger till vänster, alltså med den minst signifikanta biten först. Beskriv automaten med formler och inte med en graf. 32

5.7 Stackautomater (ganska svår) Den normala definitionen av när en stackautomat accepterar en inmatning är att den när inmatningen är slut och efter att ha följt alla passande ε-övergångar har kommit till ett accepterande tillstånd. Kalla denna definition för typ 1. Ibland är det praktiskt att använda följande definition, som vi kallar typ 2, istället. En PDA accepterar en inmatning om den när inmatningen är slut och efter att ha följt alla passande ε-övergångar har kommit till ett accepterande tillstånd och symbolen # ligger överst på stacken. a) Visa att varje språk som kan kännas igen av en DPDA av typ 2 kan kännas igen av en DPDA av typ 1. b) Visa att varje språk som kan kännas igen av en NPDA av typ 1 kan kännas igen av en NPDA av typ 2. 5.8 Att konstruera en turingmaskin (ganska svår) Funktionen monus definieras som { x 1 om x > 0, monus(x) = 0 om x = 0. Konstruera en turingmaskin som beräknar denna funktion. När turingmaskinen startar står talet x på bandet på binär form. Talet är omgivet av blanka och läs/ skrivhuvudet är placerat på första siffran i talet. 5.9 De olika automaternas förmåga Ange för vart och ett av de sju språken nedan vilken beräkningsmodell (DFA, DP- DA, etc) som är nödvändig och tillräcklig för att känna igen språket. Motivering av svaren krävs, men det behöver inte vara strikta bevis. a) S a = { ww R w {0, 1} och w R är w baklänges }. Ex: 10010000001001 S a. b) S b = { a n b m c n n, m > 0 }. Ex: aaabbbbbbbbccc S b. c) S c = { 0 n 1 n 0 n n 0 }. Ex: 000011110000 S c. d) S d = { Pascalprogram som är lexikalt men inte nödvändigtvis syntaktiskt korrekta }. Ex: end. while x if begin > mod S d. 33

e) S e = { Pascalprogram som är lexikalt och syntaktiskt korrekta }. Ex: program e; var x:integer; begin end. S e. f) S f = { Pascalprogram som skriver ut Hello world! och sedan stannar }. g) S g = { primtal < 10000 }. Ex: 17 S g. 34

Kapitel 6 Reguljära uttryck Detta avsnitt innehåller uppgifter som är relaterade till reguljära uttryck. Dessutom finns det exempel på hur man går från ett reguljärt uttryck till en ickedeterministisk ändlig automat, och därifrån vidare till en deterministisk ändlig automat. Vi visar också att de reguljära uttrycken klarar av att beskriva precis de språk som kan kännas igen av någon DFA. 6.1 Reguljära uttryck för olika typer av tal Skriv reguljära uttryck för: a) heltal, b) flyttal i decimalform, c) flyttal i exponentform. 6.2 RE NFA DFA Konstruera en ickedeterministisk ändlig automat (NFA) som känner igen språket som genereras av det reguljära uttrycket ( (xx y) xz yy ). Konstruera sedan en deterministisk ändlig automat (DFA) som känner igen samma språk. Försök minimera antalet tillstånd i automaten. 6.3 RE DFA Visa att det för varje reguljärt uttryck går att konstruera en DFA som känner igen precis det språk som det reguljära uttrycket definierar. Ett annat sätt att uttrycka samma uppgift på är: Visa att språket som utgörs av alla strängar som definieras av 35

något reguljärt uttryck är en delmängd av språket som utgörs av alla strängar som känns igen av någon DFA. 6.4 DFA RE (kuriosa) Visa att det för varje DFA går att konstruera ett reguljärt uttryck som känner igen precis det språk som DFA:n definierar. Ett annat sätt att uttrycka samma uppgift på är: Visa att språket som utgörs av alla strängar som avgörs av någon DFA är en delmängd av språket som utgörs av alla strängar definieras av något reguljärt uttryck. 6.5 Baklängesspråk (ganska svår) Bevisa att om A är ett reguljärt språk så är också {w R : w A} ett reguljärt språk, där w R är w baklänges. Med andra ord ska du visa att ett språk inte blir svårare att känna igen om man läser från höger till vänster än från vänster till höger. 6.6 CNF-formler (svår) En formel i satslogiken sägs vara i konjunktiv normalform (eller CNF) om den är på formen A 1 A 2 A k där varje A i är en disjunktion, A i = a 1 a 2 a m och varje a i är antingen en boolesk variabel eller negationen av en boolesk variabel. Negationer av negationer tillåts inte. Ett exempel på en formel i konjunktiv normalform är (p ( q) ( r)) (q r) ( p). Om vi inför en prioritetsordning,, (där har högst prioritet) för operatorerna kan formeln skrivas utan parenteser: p q r q r p En formel är satisfierbar om det finns några booleska värden för variablerna som gör formeln sann. Formeln i exemplet ovan är satisfierbar, för om p och q sätts till falskt och r till sant så blir hela formeln sann. a) Skriv ett reguljärt uttryck som beskriver mängden av CNF-formler över variablerna p, q och r. Anta att formeln inte innehåller några parenteser. b) Konstruera en DFA med alfabetet Σ = {p, q, r,,, } (där p, q och r är booleska variabler) som accepterar exakt mängden av de logiska uttryck över Σ som är i CNF. Anta att den tomma strängen inte tillhör mängden. c) Konstruera en DFA som accepterar en delmängd av ovan beskrivna mängd, nämligen mängden av de formler i CNF som blir sanna precis då p och q är sanna och r är falsk. 36

d) Förklara hur man kan bygga en NFA som accepterar exakt de formler i CNF (med variablerna p, q och r) som är satisfierbara. 6.7 Översättning av å, ä och ö Tänk dig att du håller på att programmera vid ett tangentbord som saknar dom svenska bokstäverna åäö, men att du ändå vill kunna få in dessa tecken i programmet. Då vore det praktiskt om du hade ett program som bytte ut aa mot å, ae mot ä, oe mot ö, Aa mot Å, Ae mot Ä och Oe mot Ö i kommentarer, teckenkonstanter och textsträngar, men på andra ställen (som i variabelnamn och procedurnamn) så ska inget utbyte göras. Skriv ett sådant program i Flex. Använd Flex möjlighet att märka dom reguljära uttrycken med startvillkor (se avsnittet Start conditions i Flex-manualen). Inför ett villkor för programkod, ett för kommentarer och ett för textsträngar. Förslagsvis gör du ett program som hanterar C- och C++-filer, men om du hellre vill att programmet ska hantera något annat språk får du gärna göra det. 6.8 HTML-tabeller Skriv ett Flexprogram som översätter en tabell i textform till en tabell i HTMLformat. Varje rad i indatafilen motsvarar en rad i tabellen. Första raden består av kolumnrubriker. Kolumner skiljs åt med TAB-tecknet. En HTML-tabell ska omges av texterna <TABLE BORDER> och </TABLE>. Varje tabellrad ska inledas med <TR> och varje kolumn med <TD>. Kolumnrubriker ska dock inledas med <TH>. 37

38

Kapitel 7 Grammatiker En grammatik används för att härleda den grammatiska strukturen hos något givet uttryck. En naturlig, och viktig, fråga är om det för en given grammatik finns någon tillåten indata som inte har en entydigt bestämd grammatisk struktur. Sådana grammatiker kallas flertydiga, och det visar sig att det finns språk för vilka man inte kan konstruera någon entydig grammatik som beskriver språket. 7.1 Engelsk minigrammatik Följande grammatik är given i BNF: S ::= NP V P (7.1) NP ::= the NP 1 NP 1 (7.2) NP 1 ::= ADJ NP 1 N (7.3) N ::= boy female cats dog people (7.4) V P ::= V NP V (7.5) V ::= walks talks likes hit (7.6) ADJ ::= small big furry playful (7.7) Avgör om följande strängar tillhör språket som beskrivs av grammatiken ovan om startsymbolen är S. Ge för varje sträng ett syntaxträd eller ett bevis för att strängen inte tillhör språket. a) the furry boy likes the playful big dog b) the hit playful female c) small people hit female cats 39

7.2 Funktionsanrop Utöka den vanliga entydiga grammatiken för taluttryck så att uttrycken också kan innehålla variabler och funktionsanrop med godtyckligt antal parametrar. Parametrarna får själva vara uttryck. Använd slutsymbolerna NUMBER och IDENT för tal respektive identifierare. Ett exempel på ett grammatiskt riktigt uttryck är 17+f(a*b,h(),g(g(7)))*g(a,b). 7.3 Reguljära uttryck Gör en kontextfri grammatik som definierar språket av enkla reguljära uttryck över alfabetet {A, B, C}. Reguljära uttryck är symbolerna i alfabetet och alla uttryck som kan bildas med följande regel: Om R 1 och R 2 är reguljära uttryck så är R 1 R 2, R 1 R 2, (R 1 ) och R 1 reguljära uttryck. Konstruera sedan syntaxträdet för (AB A C)A i din grammatik. 7.4 EBNF och BNF (ganska svår) EBNF är en utvidgning av BNF med operatorerna ( ), [ ] och { }. ( ) används för gruppering på samma sätt som i taluttryck, [ ] betyder att det som står mellan parenteserna antingen kan tas med eller hoppas över. { } betyder noll eller flera förekomster av det som står mellan parenteserna. Exempel på användning: E ::= E( + - )T T T ::= [T( * / )] F LISTA ::= E {, E} För mera information om EBNF, se kursboken [9] samt Wirths artikel [11]. Bevisa att EBNF inte är uttrycksfullare än BNF, dvs visa att allt som kan uttryckas i EBNF också kan uttryckas i BNF. 7.5 Matrismultiplikation (ganska svår) Eftersom multiplikationsoperatorn är associativ spelar multiplikationsordning ingen roll, men när det gäller multiplikation av matriser så kan det gå mycket snabbare om man multiplicerar i en viss ordning. Ett lämpligt sätt att ange multiplikationsordning är att använda parenteser runt varje multiplikation, till exempel (((MM)M)(MM)) där M står för en godtycklig matris. a) Mängden av de parentetiserade matrisprodukterna bildar ett språk. Konstruera en kontextfri grammatik i BNF som definierar precis detta språk. Tillåt även att man slopar det yttersta parentesparet. Exempel på godkända uttryck i språket är M(MM), ((M))M och M, men inte felparentetiserade uttryck som M)(M och M(MM)M. 40

b) Konstruera en DPDA (deterministisk stackautomat) som känner igen språket som definierats i a-uppgiften ovan. c) Går det att konstruera en NFA (ickedeterministisk ändlig automat) som känner igen språket? Motivera! d) Går det att konstruera en TM (turingmaskin) som känner igen språket? Motivera! 7.6 Inte palindrom (svår) Vi definierar språket A = {x : x {a, b} (x är inte en palindrom)}. (7.8) En palindrom definieras som en text som är samma oavsett om man läser den framifrån eller bakifrån. Exempelvis tillhör aab och aabb A men inte a, aa eller baab. a) Förklara varför språket A inte kan beskrivas av något reguljärt uttryck. b) Utforma en kontextfri grammatik för A. c) Konstruera en NPDA som känner igen A. 7.7 Vektoruttryck (ganska svår) Vektoruttryck i mekaniken kan bli ganska långa och kryptiska. Det vore en fördel om alla vektoruttryck kunde syntaxkontrolleras så att man upptäcker om man till exempel försöker kryssmultiplicera en skalär med en vektor eller dividera med en vektor. Här är ett försök till en BNF-grammatik för vektoruttryck. v står för en vektorkonstant, s står för en skalärkonstant och V är startsymbolen. V v ( V ) V S S V V V V + V V V (7.9) S s ( S ) V V S S S + S S S S / S (7.10) a) Grammatiken ovan är tyvärr inte entydig. Ge exempel på flera härledningar av ett och samma uttryck. b) Ge exempel på ett uttryck som bara kan härledas ur grammatiken om man tillämpar reglerna i en viss ordning medan det tar stopp om man försöker tillämpa reglerna i någon annan ordning. 41

c) Anta att vi gör grammatiken konfliktfri genom att införa vänsterassociativitet, dvs det som i Bison skulle heta %left. Operatorerna har alltså samma prioritet och härledningen görs från vänster. Anta vidare att all direkt vänsterrekursion har plockats bort med den vanliga metoden. Är det möjligt att skriva en rekursiv medåkningssyntaxanalysator för detta språk? Motivera ditt svar! 7.8 Kontextfri grammatik NPDA (ganska svår) Gör en NPDA som accepterar språket S, som beskrivs av följande grammatik: S A B S A (7.11) A x B y (7.12) B z (7.13) 7.9 Syntaxanalys av kontextfria grammatiker (kuriosa) Genom att generalisera metoden från exempel 7.8 kan vi visa att det för varje kontextfri grammatik finns en NPDA som accepterar det språk som beskrivs av grammatiken. Eftersom NPDA TM går det således att konstruera en algoritm som analyserar kontextfria grammatiker. Gör detta! Antag att grammatiken innehåller m regler, och att indatasträngen innehåller n slutsymboler. Vad blir algoritmens tidskomplexitet? 42

Kapitel 8 LL-analys Detta avsnitt innehåller uppgifter som är relaterade till LL-analys. Huvudidén i LLanalys är att man gissar produktionsregler uppifrån och ner. Förhoppningsvis ska dessa gissningar ge en grammatisk struktur på indata. Tyvärr visar det sig att denna typ av analys inte går att genomföra på vissa grammatiker, trots att grammatikerna i sig är entydiga. Detta går emellertid ofta att avhjälpa genom ett standardförfarande, nämligen genom att ta bort så kallade vänsterrekursioner i grammatiken. 8.1 Vänsterrekursion Ta bort alla uppenbara vänsterrekursioner från produktionsregeln A Ab ca Bd e (8.1) 8.2 First-mängder Visa att First-mängder inte kan hjälpa en uppifrånochneranalysator att välja rätt högerled när den tolkar strängen baaaaa givet grammatiken S Sa b. (8.2) 8.3 Taluttryck Tyvärr innehåller grammatiken för enkla matematiska uttryck från kursbokens [9] avsnitt 3.4 vänsterrekursioner: E E + T E T T (8.3) T T F T/F F (8.4) F (E) i (8.5) 43

En naturlig första ansats att få bort vänsterrekursionerna skulle kunna se ut som följer: E T + E T E T (8.6) T F T F/T F (8.7) F (E) i (8.8) Visa att detta inte är någon bra ansats genom att rita syntaxträd för uttrycket a b c med hjälp av båda grammatikerna och använd dessa syntaxträd för att beräkna resultatet av uttrycket då a = 3, b = 5 och c = 8. 8.4 Rekursiv medåkning (ganska svår) På vinden står lådor med motstånd märkta 1Ω, 10Ω, 100Ω,... och kondensatorer märkta 1pF, 10pF, 100pF,.... Med serie- och parallellkoppling löder man ihop RCkretsar enligt instruktioner av typen 10o+(10pF:100pF:1o)+(100o:(10pF+1pF):1pF) där plus betyder seriekoppling, kolon parallellkoppling och parenteser att kretsen betraktas som en komponent som kan användas i en större krets. Notera att SIenheten ohm betecknas med gement o. Blandkopplingar som 10o+10pF:100pF förekommer inte. Skriv en grammatik för sådana instruktioner! Använd symboler som krets, serierad, parallellrad och komponent. Skriv en rekursiv medåkningsanalysator efter grammatiken. Låt analysatorn bygga upp en trädstruktur där kretsen lagras. Skriv en funktion Kretsimpedans som beräknar impedansen för en krets lagrad som ett träd. 8.5 Nästlade kommentarer I ANSI-C definieras en kommentar som /* följt av vilka tecken som helst utom */ följt av */. Exempelvis är /* en giltig kommentar */ en giltig kommentar medan /*/* inte är det */*/ inte är det. Man kan alltså inte ha kommentarer i kommentarer. Vissa C-kompilatorer har förändrat kommentardefinitionen så att kommentarer inne i kommentarer blir tillåtna. Kalla språket av de C-program som är som korrekt kommenterade enligt den förändrade kommentardefinitionen för NK. Exempelvis tillhör /*/**/*/ och /*X/*/**/*/*/ språket NK medan /*/*X*/ och *//* inte gör det. Vidare antar vi, för att göra grammatiken enklare, att * och / endast kan förekomma i kombinationerna /* respektive */. a) Beskriv språket NK med hjälp av en BNF-grammatik. De enda slutsymbolerna som förekommer i språket är /, * och X (där X i praktiken står för vilket tecken som helst utom / och *). 44