Uppgift 1: Grundläggande Ruby och teorifrågor (5p) -------------------------------------------------- a) Nämn minst två egenskaper hos språket Ruby som gör det lämpat för att användas som bas för interna domänspecifika språk. (2p) b) Förklara kort hur du kan styra åtkomsten till metoderna i en klass. (1p) c) Vad är det för skillnad på SAX- och DOM-parsning av XML-dokument? (2p) Uppgift 2: Reguljära uttryck och textfiler (5p) ----------------------------------------------- Foobar Inc. har en webbserver som är mycket viktig för företaget. På webbservern finns en loggfil som lagrar information om alla förfrågningar som har kommit in, alltså varje gång någon hämtar en fil. Raderna i loggfilen ser ut så här 67.33.163.249 - - [16/Feb/2012:15:39:39 +0100] "GET /hdr/department/document.shtml HTTP/1.0" 200 11683 "-" "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)" Först kommer IP-adressen för den som skickade förfrågan. Därefter kommer två fält som inte används och som markeras med streck. Sedan visas tid och datum inom hakparenteser. Därefter kommer den egentliga förfrågan. Om det är en normal begäran om att bara ladda ner en fil börjar strängen med GET, följt av katalog och filnamn och sist versionen på protokollet som användes (vilket oftast är HTTP/1.0). Efter denna sträng följer två tal. Det första är statuskoden, som är 200 om allt var okej. Det andra är storleken på filen. Sist på raden är två strängar som anger varifrån användaren kom samt vilken webbläsare som används. I filen access_log finns ett utsnitt av loggfilen. Din uppgift är att skriva ett program som läser in, analyserar loggfilen och presenterar vissa slutsatser. Foobar Inc. är just nu intresserade av att veta hur mycket av vissa typer av filer som användarna laddar ner. De funderar på att lägga dessa filer på en särskild server, om det visar sig att det blir mycket trafik. Just idag är de intresserade av PDF-filer och JPG-filer. Hur många sådana laddades ner under de fem minuter som loggfilen speglar och vad var den totala storleken? Exempel på hur det kan se ut: >> read_log("access_log") 57 PDF-files (12985775 bytes) 88 JPG-files (4327304 bytes) => nil Uppgift 3: XML (6p) ------------------- "The Recursive Shakespeare Company" är en amatörteaterförening som sätter upp klassiska pjäser. Wilhelm Skakspjut är regissör och han har hittat en webbsida med manuskript till pjäser i XML-format. Nu behöver han ett program som kan hjälpa honom att räkna ut lite statistik om den pjäs han har valt, nämligen Hamlet. I den bifogade filen hamlet.xml finns hela manuset. Strukturen framgår av den DTD som finns i filen. Det finns också en förkortad version i example.xml som man kan använda för testning. (Det kan ta rätt lång tid att bearbeta hela pjäsen.) a) Först vill regissören ha en lista som talar om hur många repliker det är i varje scen (alltså antalet LINE). Det gör att han lättare kan planera repetitionerna. Denna funktion skulle t.ex. kunna bete sig så här: >> scene_length("hamlet.xml") ACT I/SCENE I (189) ACT I/SCENE II (274) ACT I/SCENE III (140) ACT I/SCENE IV (101) ACT I/SCENE V (209) ACT II/SCENE I (131) ... Exakt hur utskriften ser ut är inte intressant, så länge information om akt, scen och antal repliker finns med. (3p) b) Sedan vill regissören ha en lista över långa monologer. En monolog är ett stycke i pjäsen där en enda skådespelare har en massa repliker ensam, utan att någon annan avbryter. Det kan lätt bli ganska tråkigt om pjäsen har för långa monologer, för då tappar man dynamik. Därför behöver skådespelarna repetera dessa monologer extra mycket. William vill ha en lista över alla monologer med minst tjugo repliker i följd (alltså minst 20 st LINE i samma SPEECH). Han vill ha en funktion som gör ungefär så här: >> find_monologues("hamlet.xml") ACT I/SCENE I HORATIO (29) ACT I/SCENE I HORATIO (27) ACT I/SCENE II KING CLAUDIUS (39) ACT I/SCENE II KING CLAUDIUS (31) ACT I/SCENE II HAMLET (31) ACT I/SCENE III LAERTES (35) ACT I/SCENE III LORD POLONIUS (27) ACT I/SCENE III LORD POLONIUS (21) ACT I/SCENE IV HAMLET (26) ... Exakt hur utskriften ser ut är inte intressant, så länge information om akt, scen, skådespelare och monologens totala antal repliker finns med. Det är för övrigt i akt 3, scen 1 som Hamlets berömda monolog "Att vara eller inte vara" finns. (3p) Uppgift 4: DSL (5p) ------------------- I den här uppgiften ska vi konstruera ett litet språk för att rita mycket enkel ASCII-grafik. Varje "program" i språket måste börja med att man anger hur stor yta man ska rita på, t.ex.: start 10,10 Detta gör att vi skapar ett rutnät med 10 x 10 tecken (första talet är bredden och andra talet är höjden). Vi tänker oss att vi har en "penna" som kan rita ett tecken i varje ruta. När vi precis har skapat rutnätet svävar pennan ovanför position (0, 0) vilket motsvarar övre vänstra hörnet av rutnätet. Vi kan "sänka" pennan med kommandot: down "W" Detta ritar ett "W" på den position som är under pennan. Om vi därefter gör en förflyttning kommer pennan att fortsätta rita i de rutor vi kommer till. Vi kan förflytta oss i fyra olika riktningar, motsvarande väderstrecken. Exempel: e 5 s 2 w 2 n 1 Ovanstående kommandon flyttar pennan österut (höger) fem steg, därefter söderut (nedåt) två steg, o.s.v. Om pennan är nedsänkt ritas det tecken vi har angivit, annars händer inget annat än att vi förflyttar oss. Om vi inte ger något argument till e, s, w eller n går vi ett steg. Vi kan lyfta pennan med kommandot: up Utöver dessa sju grundkommandon kan man ange ett filnamn som innehåller fler kommandon. Denna fil öppnas och körs, vilket gör att vi på det sättet kan skapa ett slags enkla funktioner som vi kan upprepa flera gånger. Uppgiften går ut på att implementera en klass som kan tolka det här lilla rit-språket som ett internt DSL i Ruby. De två filerna draw.rb och square.rb innehåller ett exempel som ytterligare visar hur det är tänkt att funka. Om man "kör" dem bör vi se följande resultat: >> Drawing.make("draw.rb") ------------ |#### | |# # | |# # | |#### | |**** | |* * | |* * | |**** | | | | | ------------ De yttre strecken hör inte till själva "ritningen" utan är bara en ram för att vi lättare ska se vad vi ritat. Definiera klassen Drawing som, med hjälp av DSL-tekniker, kan tolka vårt enkla rit-språk och producera ASCII-grafik enligt ovanstående exempel. Uppgift 5: Constraint-nätverk (5p) ---------------------------------- I filen constraint_networks.rb finns kod för att bygga restriktionsnät som vi diskuterade i samband med seminarie 4. Vi tänker oss att du har löst seminarieuppgifterna och kan skapa ett nät för att omvandla mellan Celsius och Fahrenheit. Användningen av denna omvandlare kunde se ut ungefär så här: irb(main):176:0> c,f=create_temperature_network [#, #] irb(main):177:0> c.user_assign 0 "ok" irb(main):178:0> f.value 32 Antag att vi vill använda intervall istället för heltal. Vad blir 18-20 grader Celsius i Fahrenheit? Då vill vi kunna göra så här: irb(main):179:0> c.user_assign 18..20 "ok" irb(main):180:0> f.value 64..68 irb(main):181:0> Vad behöver göras för att uppnå detta? Du behöver inte implementera temperaturomvandlingen igen, utan skriv bara kod som gör att de constraints som finns i filen kan hantera även intervall. När vi skriver 18..20 avses alltså en instans av klassen Range. Uppgift 6: Parsning (6p) ------------------------ I filen rdparse.rb finns den parser som vi jobbat med tidigare i kursen, inklusive exempelspråket i klassen DiceRoller. Nu ska vi utöka DiceRoller med enkla definitioner. Vi vill kunna spara undan komplicerade uttryck och ge dem ett namn, för att sedan lätt kunna köra dem flera gånger. I praktiken vill vi att det ska funka så här: irb(main):004:0> DiceRoller2.new.roll [diceroller] def a d6+3 => ok [diceroller] #a => 5 [diceroller] #a => 7 [diceroller] #a => 4 [diceroller] def b 3*d4+d20 => ok [diceroller] #b => 17 [diceroller] #b => 16 [diceroller] 3+4d5 => 13 [diceroller] #c => Error: Saved expression c not found! Vi har alltså utökat språket med två konstruktioner. a) Konstruktionen 'def' som tar ett namn och ett uttryck. Uttrycket sparas undan på lämpligt sätt så att man kan plocka fram det senare med hjälp av namnet. b) Konstruktionen '#' som tar enbart ett namn och som kör det sparade uttrycket igen. Förändra DiceRoller-klassen så att den klarar av dessa två saker.