Fra Minila til Flink via Java

Like dokumenter
Velkommen til INF2100

Bakgrunnen for INF2100. Velkommen til INF2100. Prosjektet. Hva gjør en kompilator?

Hva er kompilering? Dagens tema. En kompilator En kompilator leser Minila koden og lager Flok koden.

Velkommen til INF2100 Jeg er Dag Langmyhr

Dagens tema: INF2100. Syntaksanalyse. Hva annet gjør en kompilator? Sjekking av navnebruk. Testutskrifter

Dagens tema: 12 gode råd for en kompilatorskriver

INF Repetisjon: Hvordan bygge treet og analysere? 8. september Typisk situasjon. De problematiske syntaks-diagrammene

Syntaksanalyse. Dagens tema: Språkdiagrammene Jernbanediagrammene er et ypperlig utgangspunkt for å analysere et program: INF2100 INF2100 INF2100

NOTAT (pensum!) Javas klasse-filer, byte-kode og utførelse. INF 5110, 10/5-2011, Stein Krogdahl

Litt om Javas class-filer og byte-kode

Hvordan skrive Flok og Flass kode? I mange tilfelle er det svært enkelt:

NOTAT (pensum!) Javas klasse-filer, byte-kode og utførelse

UNIVERSITETET I OSLO Institutt for informatikk. RusC og Rask. Kompendium til INF2100. Stein Krogdahl Dag Langmyhr

Dagens tema: 12 gode råd for en kompilatorskriver. Sjekking av navn. Lagring av navn. Hvordan finne et navn?

UNIVERSITETET I OSLO Institutt for informatikk. RusC og Rask. Kompendium til INF2100. Stein Krogdahl Dag Langmyhr

Velkommen til INF Kompilatorteknikk

UTKAST. RusC og Rask. Kompendium til INF2100. Stein Krogdahl Dag Langmyhr. UNIVERSITETET I OSLO Institutt for informatikk

Javas klasse-filer, byte-kode og utførelse (og litt om C# sin CIL-kode)

Dagens tema: INF2100. Utvidelser av Minila array-er. tegn og tekster. Flass- og Flokkode. prosedyrer. Prosjektet struktur. feilhåndtering.

Velkommen til INF5110 Kompilatorteknikk

Obligatorisk oppgave 1 INF1020 h2005

Velkommen til INF Kompilatorteknikk

Råd nr 1: Start nå! Det tar typisk timer å programmere Del 1 om man ikke har gjort slikt før. Dagens tema:

Generiske mekanismer i statisk typede programmeringsspråk

Oppsummering av Uke 3. MAT1030 Diskret matematikk. Binære tall. Oppsummering av Uke 3

Hovedansvarlig. Symbolgenerator. Tregenerator. Litt mer kompliserte setninger med betingelser

Kodegenerering del 3: Tilleggsnotat fra AHU Samt litt om class-filer og byte-kode INF5110 V2007. Stein Krogdahl, Ifi UiO

Mer om representasjon av tall

Velkommen til INF Kompilatorteknikk

Velkommen til INF Kompilatorteknikk

Litt om Javas håndtering av tall MAT-INF 1100 høsten 2004

Velkommen til INF Kompilatorteknikk

MAT-INF 1100: Obligatorisk oppgave 1

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000

2 Om statiske variable/konstanter og statiske metoder.

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000 v2008

Programmeringsspråket C

INF1000: noen avsluttende ord

MAT-INF 1100: Obligatorisk oppgave 1

Kap.4, del 2: Top Down Parsering Kap. 5, del 1: Bottom Up Parsing INF5110, 7/ Legger ut en oppgave til kap. 4 (se beskjed).

MAT-INF 1100: Obligatorisk oppgave 1

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000 h2006

Reelle tall på datamaskin

Kap. 4: Ovenfra-ned (top-down) parsering

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000

INF oktober Dagens tema: Uavgjørbarhet. Neste uke: NP-kompletthet

MAT-INF 1100: Obligatorisk oppgave 1

Velkommen til INF Kompilatorteknikk

Oblig2 - obligatorisk oppgave nr. 2 (av 4) i INF1000 v2009

Dagens tema. Hva er kompilering? Anta at vi lager dette lille programmet doble.rusc (kalt kildekoden): Hva er kompilering?

En snarvei til INF2100

Dagens tema Syntaks (kapittel Komp. 47, kap. 1 og 2)

Programmeringsspråket C Del 3

Programmeringsspråket C Del 3

Litt om kompilering og interpretering. Dagens tema Syntaks (kapittel Komp. 47, kap. 1 og 2) Syntaks og semantikk

Dagens tema. Hva er kompilering? Anta at vi lager dette lille programmet (kalt kildekoden): Hva er kompilering?

Velkommen til INF Kompilatorteknikk

Programmeringsspråket C Del 3

Vi som skal undervise. MAT1030 Diskret matematikk. Hva er diskret matematikk? Hva er innholdet i MAT1030?

Velkommen til. IN1010 Objektorientert programmering Våren 2018

Hjemmeeksamen 2 i INF3110/4110

Ordliste. Obligatorisk oppgave 1 - Inf 1020

Velkommen til INF Introduksjon til operativsystemer og datakommunikasjon

KONTROLLSTRUKTURER. MAT1030 Diskret matematikk. Kontrollstrukturer. Kontrollstrukturer. Eksempel (Ubegrenset while-løkke)

Innhold uke 4. INF 1000 høsten 2011 Uke 4: 13. september. Deklarasjon av peker og opprettelse av arrayobjektet. Representasjon av array i Java

Programmeringsspråket C Del 3

Oppgave 1 - Linux kommandolinje (%)

Skanning del I INF /01/15 1

TDT4110 Informasjonsteknologi, grunnkurs Uke 35 Introduksjon til programmering i Python

Anbefalt litteratur: Pensum-bøker: Forelesere: Velkommen til INF Introduksjon til operativsystemer og datakommunikasjon

public static <returtype> navn_til_prosedyre(<parameter liste>) { // implementasjon av prosedyren

2 Om statiske variable/konstanter og statiske metoder.

MAT1030 Diskret matematikk

C< og kompilatoren hans

Programmeringsspråket C Del 2

Programmeringsspråket C Del 2

Argumenter fra kommandolinjen

Programmeringsspråket C Del 2

Scanning - I Kap. 2. Hva scanneren gjør

Kort om meg. INF1000 Uke 2. Oversikt. Repetisjon - Introduksjon

MAT1030 Diskret Matematikk

Kort om kursene INF1100 og MAT-INF1100L

Oversikt. INF1000 Uke 1 time 2. Repetisjon - Introduksjon. Repetisjon - Program

Obligatorisk Innlevering 2

Velkommen til MAT1030!

MAT1030 Diskret Matematikk

Forelesning 14. Rekursjon og induksjon. Dag Normann februar Oppsummering. Oppsummering. Beregnbare funksjoner

Læringsmål og pensum. v=nkiu9yen5nc

Forelesning 2. Flere pseudokoder. Representasjoner av tall. Dag Normann januar 2008 KONTROLLSTRUKTURER. Kontrollstrukturer. Kontrollstrukturer

IN1010 Objektorientert programmering Våren 2019

MAT1030 Diskret matematikk

Innledning. MAT1030 Diskret matematikk. Kapittel 11. Kapittel 11. Forelesning 33: Repetisjon

UNIVERSITETET I OSLO

Kap. 5, Del 3: INF5110, fra 1/3-2011

notater Gule lapper Mine Et praktisk eksempel med objekter IT2 Læreplansmål Gløer Olav Langslet Sandvika VGS

EKSAMENSFORSIDE SKRIFTLIG EKSAMEN

INF okt. 2004

OPPGAVE 1 OBLIGATORISKE OPPGAVER (OBLIG 1) (1) Uten å selv implementere og kjøre koden under, hva skriver koden ut til konsollen?

Transkript:

UNIVERSITETET I OSLO Institutt for informatikk Fra Minila til Flink via Java Reisehåndbok for INF2100 Stein Krogdahl Dag Langmyhr Else Nordhagen Høsten 2006

Fra Minila til Flink via Java Stein Krogdahl Dag Langmyhr Else Nordhagen Høsten 2006

Forord Dette kompendiet er laget for kurset INF2100 Prosjektoppgave i programmering ved Institutt for informatikk, Universitetet i Oslo. Liknende kurs (med betegnelsene IN102 og INF111) er blitt holdt med en tilsvarende obligatorisk oppgave («Lag en kompilator for Minila») siden tidlig på 1980-tallet. Det har imidlertid over denne tiden skjedd en del forandringer både med detaljer i oppgaven, i løsningsopplegget og i kompendiet. Stein Krogdahl laget og beskrev den opprinnelige oppgaven, mens Else Nordhagen ca 10 år senere la om løsningsopplegget i mer objektorientert retning, og la om kompendiet tilsvarende. Høsten 1999 ble løsningsopplegget lagt om på nytt fordi instituttet gikk over til programmeringsspråket Java i sin grunnundervisning. Kompendiet slik det nå foreligger (høsten 2006) er igjen noe omarbeidet av Dag Langmyhr i og med overgangen fra INF111 til INF2100. Programmeringsspråket Minila ble også samtidig utvidet litt. Hvis (når?) dere finner feil, eller på annen måte har kommentarer til dette kompendiet, vil disse bli mottatt med takk av kursledelsen. Tiden er alltid knapp når man skal lage en ny versjon av kompendiet, og det er jo bare å innse at det vil forekomme inkonsistenser, og at det er rom for forbedringer både med hensyn til språk og den generelle strukturen. Trykkfeil vil man nok også finne en del av, men de fleste er forhåpentligvis ikke verre enn at man kan forstå hva som menes. Mange personer har bidratt til dette kompendiet ned gjennom tidene. I tillegg til de «offisielle» forfatterne bør følgende personer takkes for å ha bidratt, gjerne mens de har vært forelesere eller gruppelærere for kurset en eller flere ganger: Odd Harry Arnesen, Olav Andre Brevik, Anders Ellefsrud, Lars Marius Garshol, Johannes Grødem, Ole Bjørn Hessen, Sverre Hvammen Johansen, Edvard Løkketangen, Ellen Munthe-Kaas, Lars Tafjord, og Christian Aastorp. Blindern, oktober 2006 Stein Krogdahl Dag Langmyhr Side iii

Side iv FORORD

Innhold Forord Innhold Tabeller Figurer iii v ix xi 1 Innledning 1 1.1 Hva er kurset INF2100?.......................... 1 1.2 Hvorfor oppgaven er å lage en kompilator................ 1 1.3 Litt om kompilatorer og liknende verktøy................ 2 1.3.1 Preprosessorer.......................... 3 1.3.2 Interpretering........................... 4 1.3.3 Kompilering og kjøring av Java-programmer.......... 4 1.3.4 Forholdene i vår Minila-kompilator............... 5 1.4 Språkene i oppgaven............................ 5 1.4.1 Programmeringsspråket Minila.................. 5 1.4.2 Datamaskinen Flink og dens maskinspråk............ 5 1.4.3 Assembleren Flass........................ 6 1.4.4 Oversikt over de ulike språkene i oppgaven.......... 8 1.5 Oppgaven og dens tre deler........................ 8 1.5.1 Del 0................................ 8 1.5.2 Del 1................................ 9 1.5.3 Del 2................................ 9 1.6 Programmering av lister, trær etc..................... 9 1.7 Krav til samarbeid og gruppetilhørighet................. 9 1.8 Kontroll av innlevert arbeid........................ 10 1.9 Delta på gruppene............................. 10 2 Slik brukeren ser Minila-kompilatoren 13 2.1 Kompilatorens reaksjon på feil...................... 13 2.2 Feilmeldinger og listing med testutskrifter................ 14 2.3 Produksjon av Flok-kode......................... 15 2.4 Testutskrifter fra kompilatoren...................... 15 3 Språket Minila 17 3.1 Minila.................................... 17 3.2 Oppgaver.................................. 25 4 Maskinen Flink og Flok-formatet 29 4.1 Maskinen Flink............................... 29 4.1.1 Instruksjonslageret........................ 29 4.1.2 Datalageret............................ 30 4.1.3 Registrene............................. 30 4.1.4 Instruksjonene.......................... 30 4.1.5 Maskinen Flink-0......................... 31 4.2 Hva skiller Flink fra en «ekte» maskin.................. 31 Side v

INNHOLD 4.2.1 Assemblerspråk og Flass..................... 34 4.3 Flok..................................... 34 4.3.1 Kommentarer........................... 35 5 Assemblerspråket Flass 37 5.1 Oppgaver.................................. 40 6 Hvordan lage Flink-kode 41 6.1 Deklarasjoner av variable og array-er.................. 41 6.1.1 Tekstkonstanter.......................... 41 6.2 Beregning av uttrykk............................ 42 6.3 Tilordnings-setningen........................... 44 6.4 Alle Outxxx-setningene......................... 44 6.5 Betingelser i If-, Repeat- og While-setninger............... 44 6.6 Intext-setningen............................. 44 6.7 Prosedyredeklarasjon og call-setning................... 47 6.8 Et fullstendig Minila-program....................... 48 6.9 Oppgaver.................................. 48 7 Hovedstrukturen i kompilatoren 51 7.1 Moduler, objekter og klasser....................... 51 7.2 En mulig moduloppdeling av kompilatoren................ 52 7.3 MainResponsible.............................. 53 7.3.1 Data den trenger ved oppstart.................. 54 7.3.2 Viktige data den bør ha i lokale variable............. 54 7.3.3 Viktige metoder den må tilby utad................ 54 7.3.4 Testutskrifter den skal kunne gi................. 54 7.4 ListingResponsible............................. 55 7.4.1 Data den trenger ved oppstart.................. 55 7.4.2 Viktige data den bør ha i lokale variable............. 55 7.4.3 Viktige metoder den må tilby utad................ 55 7.4.4 Testutskrifter den skal kunne gi................. 55 7.5 LineGenerator............................... 55 7.5.1 Data den trenger ved oppstart.................. 55 7.5.2 Viktige data den bør ha i lokale variable............. 56 7.5.3 Viktige metoder den må tilby utad................ 56 7.5.4 Testutskrifter den skal kunne gi................. 56 7.6 CharacterGenerator............................ 56 7.6.1 Data den trenger ved oppstart.................. 56 7.6.2 Viktige data den bør ha i lokale variable............. 56 7.6.3 Viktige metoder den må tilby utad................ 56 7.6.4 Testutskrifter den skal kunne gi................. 57 7.7 SymbolGenerator............................. 57 7.7.1 Data den trenger ved oppstart.................. 57 7.7.2 Viktige data den bør ha i lokale variable............. 57 7.7.3 Viktige metoder den må tilby utad................ 58 7.7.4 Testutskrifter den skal kunne gi................. 58 7.8 TreeGenerator............................... 58 7.8.1 Data den trenger ved oppstart.................. 58 7.8.2 Viktige data den bør ha i lokale variable............. 58 7.8.3 Viktige metoder den må tilby utad................ 59 Side vi

INNHOLD 7.8.4 Testutskrifter den skal kunne gi................. 59 7.9 CodeGenerator.............................. 59 7.9.1 Data den trenger ved oppstart.................. 59 7.9.2 Viktige data den bør ha i lokale variable............. 59 7.9.3 Viktige metoder den må tilby utad................ 59 7.9.4 Testutskrifter den skal kunne gi................. 60 7.10 FlinkRepresentation............................ 60 7.10.1 Data den trenger ved oppstart.................. 60 7.10.2 Viktige data den bør ha i lokale variable............. 60 7.10.3 Viktige metoder den må tilby utad................ 60 7.10.4 Testutskrifter den skal kunne gi................. 60 7.11 Oppdelingen av arbeidet i Del-0, Del-1 og Del-2............ 61 7.11.1 Del-0................................ 61 7.11.2 Del-1................................ 61 7.11.3 Del-2................................ 61 8 Gode vaner når man skriver programmer 63 8.1 Valg av navn på klasser, metoder, variable osv.............. 63 8.2 Om kommentarer i programmet..................... 65 8.2.1 Ikke kommentér på for lavt nivå................. 66 8.2.2 Bedre med færre og større kommentarer........... 66 8.2.3 Angi invarianter i kommentarene................ 66 8.2.4 Dokumentasjon av klasser.................... 67 8.2.5 Avsluttende kommentarer om kommentering......... 67 8.3 Det grafisk utseende av programmet................... 67 9 Kommentarer til Del-0-skissen 71 10 Gjennomføringen av Del-1 75 10.1 Syntaktisk analyse ved «recursive descent»............... 77 10.2 Treet representerer et Minila-program.................. 79 10.2.1 Litt om selve byggingen av treet................. 80 10.2.2 Noen flere detaljer i treet.................... 80 10.2.3 Representasjon av uttrykk.................... 81 10.2.4 Representasjon av deklarasjoner................. 82 10.3 Sjekking av korrekt navnebruk etc.................... 83 10.3.1 Hvordan finne deklarasjonsnoden til et navn.......... 84 10.4 Testutskrifter................................ 85 10.4.1 A-testutskrift........................... 85 10.4.2 P-testutskrift........................... 86 10.5 Det store bildet.............................. 87 10.5.1 Ikke bland klassetreet og objekttreet.............. 89 10.6 Noen program-skisser........................... 89 10.6.1 Skisser til analyse-metoder.................... 89 10.6.2 Skisser til P-testutskrift...................... 92 11 Gjennomføringen av Del-2 95 11.1 Endringer i «gamle» deler av programmet................ 95 11.2 Flink-representanten............................ 96 11.2.1 En skisse til hovedstruktur.................... 97 11.3 CodeGenerator.............................. 99 Side vii

INNHOLD 11.3.1 Om selve kodegenereringen................... 100 11.3.2 K-testutskrift........................... 102 A Løsningsforslag 103 Side viii

Tabeller 3.1 Reserverte ord i Minila.......................... 25 4.1 Instruksjonskodene for Flink....................... 32 6.1 Oversettelse av datadeklarasjoner..................... 42 6.2 Oversettelse av første operand i et uttrykk............... 43 6.3 Oversettelse av operasjoner i uttrykk.................. 43 6.4 Oversettelse av tilordningssetningen................... 45 6.5 Oversettelse avoutxxx-setningene................... 46 6.6 Oversettelse av betingelser i If-, Repeat- og While-setninger..... 46 6.7 Sammenheng mellom rel og J xrel I i betingelsene.......... 46 6.8 Oversettelse av If-setningene....................... 46 6.9 Oversettelse av Repeat-setningen.................... 47 6.10 Oversettelse av While-setningen..................... 47 6.11 Oversettelse avintext-setningen.................... 47 6.12 Oversettelse av prosedyrekall og -deklarasjon.............. 48 6.13 Oversettelse av et fullstendig program.................. 48 Side ix

Side x TABELLER

Figurer 1.1 Sammenhengen mellom Minila, Flass, Flok og Flink........... 7 3.1 Eksempel på Minila-kode......................... 18 3.2 Tegnsettet ISO 8859-1........................... 19 3.3 Syntaksdiagrammer for Minila....................... 20 3.4 Syntaksdiagrammer for Minila (forts)................... 21 3.5 Syntaksdiagrammer for Minila (forts)................... 22 3.6 Syntaksdiagrammer for Minila (forts)................... 22 3.7 Syntaksdiagrammer for Minila (forts)................... 22 3.8 Syntaksdiagrammer for Minila (forts)................... 23 3.9 Syntaksdiagrammer for Minila (forts)................... 23 3.10 Syntaksdiagrammer for Minila (forts)................... 23 3.11 Syntaksdiagrammer for Minila (forts)................... 23 4.1 Eksempel på Flok-kode.......................... 35 7.1 Hovedstrukturen av kompilatoren.................... 52 9.1 Eksempel på S-testutskrift......................... 73 10.1 Idéskisse til en trestruktur som representerer en while-setning i Minila. 76 10.2 Idéskisse til trestruktur som representerer en Minila-prosedyre med deklarasjons-pekere for hver «bruksforekomst» av navn..... 77 Side xi

Side xii

Teori er når ingenting virker og alle vet hvorfor. Praksis er når allting virker og ingen vet hvorfor. Kapittel 1 Innledning I dette kurset kombineres teori og praksis ingenting virker og ingen vet hvorfor. Forfatterne 1.1 Hva er kurset INF2100? Kurset INF2100 har betegnelsen Prosjektoppgave i programmering, og ideen med dette kurset er å ta med studentene på et så stort programmeringsprosjekt som mulig innen rammen av de ti studiepoeng kurset har. Grunnen til at vi satser på ett stort program er at de fleste ting som har å gjøre med strukturering av programmer, oppdeling i moduler etc, ikke oppleves som meningsfylte eller viktige før programmene får en viss størrelse og kompleksitet. Det som sies om slike ting i begynnerkurs får derfor lett preg av litt livsfjern «programmeringsmoral» fordi man ikke ser behovet for denne måten å tenke på i de små oppgavene man der rekker å gå gjennom. Ellers er programmering noe man trenger trening for å bli sikker i. Dette kurset vil derfor ikke innføre så mange nye begreper omkring programmering, men i stedet forsøke å befeste det man allerede har lært, og demonstrere hvordan det kan brukes i forskjellige sammenhenger. «Det store programmet» som skal lages i løpet av INF2100 er en kompilator. En kompilator oversetter fra ett datamaskinspråk til et annet, vanligvis fra et såkalt høynivåspråk til et som datamaskinens elektronikk kan utføre direkte. Nedenfor skal vi se litt på hvorfor det å lage en kompilator er valgt som tema for oppgaven, og hva en kompilator er. I dette kompendiet beskrives stort sett bare selve programmeringsoppgaven som skal løses. I tillegg til dette kan det komme ytterligere krav, for eksempel angående bruk av verktøy eller skriftlige arbeider som skal leveres. Dette vil i så fall bli opplyst på forelesningene og på kursets hjemmside i god tid. 1.2 Hvorfor oppgaven er å lage en kompilator Selv om vi konsentrerer dette kurset omkring ett større program vil ikke dette kunne bli noe virkelig stort program. Ute i den «virkelige» verden blir programmer fort vekk på flere hundre tusen eller endog millioner linjer, og det er først når man skal i gang med å skrive, og ikke minst senere gjøre endringer i, slike programmer, at strukturen av programmene blir helt avgjørende. Det programmet vi skal lage i dette kurset vil typisk bli på et par tre tusen linjer. Når det skulle velges «tema» for en programmeringsoppgave til dette kurset var det først og fremst to kriterier som var viktige: Side 1

KAPITTEL 1 INNLEDNING Oppgaven må være overkommelig å programmere innen kursets ti studiepoeng. Programmet må angå en problemstilling som studentene kjenner, slik at det ikke går bort verdifull tid til å forstå hensikten med og omgivelsene til programmet. I tillegg til dette kan man ønske seg et par ting til: Det å lage et program innen et visst anvendelsesområde gir vanligvis også bedre forståelse av området selv. Det er derfor også ønskelig at anvendelsesområdet er hentet fra databehandling slik at denne bieffekten gir øket forståelse av faget selv. Problemområdet bør ha så mange interessante variasjoner at det kan være en god kilde til øvelsesoppgaver som kan belyse hovedproblemstillingen. Utfra disse kriterier synes ett felt å peke seg ut som spesielt fristende, nemlig det å skrive en kompilator, altså skrive et program som oppfører seg omtrent som for eksempel en Java-kompilator eller en C-kompilator. Dette er en type verktøy som alle som har arbeidet med programmering har vært borte i, og som det også er verdifullt for de fleste å lære litt mer om. Det å skrive en kompilator vil også for de fleste i utgangspunktet virke som en stor og uoversiktlig oppgave. Noe av poenget med kurset er å demonstrere at med en hensiktsmessig oppdeling av programmet i deler som hver tar ansvaret for en avgrenset del av oppgaven, så kan både de enkelte deler og den helheten de danner bli høyst medgjørlig. Det er denne erfaringen, og forståelsen av hvordan slik oppdeling kan gjøres på et reelt eksempel, som er det viktigste studentene skal få med seg fra dette kurset. Vi skal i neste avsnitt se litt mer på hva en kompilator er, og hvordan den står i forhold til liknende verktøy. Det vil da også raskt bli klart at det å skrive en kompilator for et «ekte» programmeringsspråk som skal oversette til maskinspråket til en «ekte» maskin vil bli en alt for omfattende oppgave. Vi skal derfor forenkle oppgaven en del, for eksempel ved å lage vårt eget lille programmeringsspråk Minila, og ved å definere vår egen maskin Flink som kompilatoren skal oversette til. Vi skal i det følgende se litt nærmere på disse og andre elementer som inngår i oppgaven. 1.3 Litt om kompilatorer og liknende verktøy Side 2 De fleste som starter på kurset INF 2100 har vel ikke noen full oversikt av hva en kompilator er, og hvilken funksjon den har i forbindelse med et programmeringsspråk. Dette vil forhåpentligvis bli mye klarere i løpet av dette kurset, men for å sette scenen skal vi gi en kort forklaring her. Grunnen til at man i det hele tatt har kompilatorer er at det er høyst upraktisk å bygge datamaskiner slik at de direkte ut fra sin elektronikk kan utføre et program skrevet i et såkalt høynivå programmeringsspråk (som for eksempel Java, C, C++ eller Perl). I stedet er datamaskiner bygget slik at de kan utføre et begrenset repertoar av nokså enkle instruksjoner (og det blir derved en begrenset oppgave å lage elektronikk som kan utføre disse). Til gjengjeld kan

1.3 LITT OM KOMPILATORER OG LIKNENDE VERKTØY datamaskiner raskt utføre lange sekvenser av slike instruksjoner, grovt sett med en hastighet av 300 3000 millioner instruksjoner per sekund. For å kunne få utført programmer som er skrevet for eksempel i Java, lages det derfor programmer som kan oversette et Java-program til en tilsvarende sekvens av maskininstruksjoner for en gitt maskin. Det er slike oversettelsesprogrammer som kalles kompilatorer. En kompilator er altså et helt vanlig program som leser data inn, og som leverer data fra seg. Dataene det leser inn er et tekstlig program (i det programmeringsspråket denne kompilatoren skal oversette fra), og det den leverer fra seg er en sekvens av maskininstruksjoner for den aktuelle maskinen. Disse maskininstruksjonene vil kompilatoren vanligvis legge på en fil i et passelig format, med tanke på at de senere kan kopieres inn i en maskin og bli utført. Det settet med instruskjoner som en datamaskin kan utføre direkte i elektronikk kalles maskinens maskinspråk, og programmer i dette språket kalles maskinprogrammer eller maskinkode. En kompilator må også sjekke at det programmet den får inn overholder alle reglene for det aktuelle programmeringsspråket. Om det ikke gjør det må det gis feilmeldinger, og da lages det som regel heller ikke noe maskinprogram. For å få begrepet kompilator i perspektiv skal vi se litt på et par liknende måter å ordne seg på, og hvordan disse skiller seg fra tradisjonelle kompilatorer. 1.3.1 Preprosessorer I stedet for å kompilere til en sekvens av maskininstruksjoner finnes det også noen kompilatorer som oversetter til et annet programmeringsspråk på samme «nivå». For eksempel kunne man tenke seg å oversette fra Java til C++, for så å la en C++-kompilator oversette det videre til maskinkode. Vi sier da gjerne at denne Java-«kompilatoren» er en preprosessor til C++-kompilatoren. Mer vanlig er det å velge denne løsningen dersom man i utgangspunktet vil bruke et bestemt programmeringsspråk, men ønsker noen spesielle utvidelser, enten på grunn av en bestemt oppgave eller fordi man tror det kan gi språket nye generelle muligheter. En preprosessor behøver da bare ta tak i de spesielle utvidelsene, og oversette disse til konstruksjoner i grunnutgaven av språket. Et eksempel på et språk der de første kompilatorene ble laget på denne måten, er C++. C++ var i utgangspunktet en utvidelse av språket C, og utvidelsen bestod i å legge til objektorienterte begreper (så som klasser, subklasser og objekter) hentet fra språket Simula. Denne utvidelsen ble i første omgang implementert ved en preprosessor som oversatte alt til ren C. I dag er imidlertid de fleste kompilatorer for C++ skrevet som selvstendige kompilatorer som oversetter direkte til maskinkode. En viktig ulempe ved å bruke en preprosessor at det lett blir tull omkring feilmeldinger og tolkningen av disse. Siden det programmet preprosessoren leverer fra seg likevel skal gjennom en full kompilering etterpå, lar man vanligvis være å gjøre en full programsjekk i preprosessoren. Dermed kan den slippe gjennom feil, som i andre omgang resulterer i feilmeldinger fra den avsluttende kompileringen. Problemet blir da at disse vanligvis ikke vil referere til linjenummerene i brukerens opprinnelige program, og de kan også på andre måter virke nokså uforståelige for vanlige brukere. Side 3

KAPITTEL 1 INNLEDNING 1.3.2 Interpretering Det er også en annen måte å utføre et program skrevet i et passelig programmeringsspråk på, og den kalles interpretering. I stedet for å skrive en kompilator som kan oversette programmer i det aktuelle programmeringsspråket til maskinspråk, skriver man en såkalt interpret (eller interpretator?). Dette er et program som (i likhet med en kompilator) leser det aktuelle programmet linje for linje, men som i stedet for å produsere maskinkode rett og slett gjør det som programmet foreskriver skal gjøres. Den store forskjellen blir da at en kompilator bare leser (og oversetter) hver linje én gang, mens en interpret må lese (og utføre) hver linje på nytt hver eneste gang den skal utføres for eksempel i en løkke. Interpretering går derfor generelt en del tregere under utførelsen, men man slipper å gjøre noen kompilering. En del språk er (eller var opprinnelig) siktet spesielt inn på interpretering, det gjelder for eksempel Basic og Lisp. Det finnes imidlertid nå kompilatorer også for disse språkene. En type språk som nesten alltid blir interpretert er kommandospråk til operativsystemer; et slikt eksempel er Bash. Interpretering kan gi en del fordeler med hensyn på fleksibel og gjenbrukbar kode. For å utnytte styrkene i begge teknikkene, er det laget systemer som kombinerer interpretering og kompilering. Noe av koden kompileres helt, mens andre kodebiter oversettes til et mellomnivå-språk som er bedre egnet for interpretering og som da intepreteres under kjøring. Smalltalk, Perl og Python er eksempler på språk som ofte er implementert slik. Interpretering kan også gi fordeler med hensyn til portabilitet, og som vi skal se under er dette utnyttet i forbindelse med vanlig implementasjon av Java. 1.3.3 Kompilering og kjøring av Java-programmer Side 4 En av de opprinnelige ideene ved Java var knyttet til datanett ved at et program skulle kunne kompileres på én maskin for så å kunne sendes over nettet til en hvilkensomhelst annen maskin (for eksempel som en såkalt «applet») og bli utført der. For å få til dette definerte man en «tenkt datamaskin», kalt JVM («Java Virtual Machine»), og lot kompilatorene produsere maskinkode (gjerne kalt byte-kode) for denne maskinen. Det er imidlertid ingen datamaskin som har elektronikk for direkte å utføre slik byte-kode, og maskinen der programmet skal utføres må derfor ha et program som simulerer JVM-maskinen og dens utføring av byte-kode. Vi kan da gjerne si at et slikt simuleringsprogram interpreterer maskinkoden til JVM-maskinen. I dag har for eksempel de fleste nettlesere (Mozilla, Opera, Explorer osv) innebygget en slik JVM-interpret for å kunne utføre Java-applets når de får disse (ferdig kompilert) over nettet. Slik interpretering av maskinkode går imidlertid alltid en del saktere enn om man hadde oversatt til «ekte» maskinkode og kjørt den direkte på «hardware». Typisk kan dette for Javas byte-kode gå 1,5 til 4 ganger så sakte. Etter hvert som Java er blitt mer populært har det derfor også blitt behov for systemer som kjører Java-programmer raskere, og den vanligste måten å gjøre dette på er på er å utstyre JVM-er med såkalt JIT-kompilering («Just-In-Time-kompilering»). Dette vil si at man i stedet for å interpretere byte-koden oversetter den videre til den aktuelle maskinkode umiddelbart før programmet startes opp. Dette kan

1.4 SPRÅKENE I OPPGAVEN gjøres for hele programmer, eller for eksempel for klasse etter klasse etterhvert som de tas i bruk første gang. Man kan selvfølgelig også oversette Java-programmer på mer tradisjonell måte direkte fra Java til maskinkode for en eller annen faktisk maskin, og slike kompilatorer finnes og kan gi meget rask kode. Om man bruker en slik kompilator, mister man imidlertid fordelen med at det kompilerte programmet kan kjøres på alle systemer. 1.3.4 Forholdene i vår Minila-kompilator I den oppgaven som skal løses i INF2100 skal vi i utgangspunktet lage en tradisjonell kompilator for språket Minila. Det å oversette til maskinkoden for en ekte maskin ville imidlertid føre for langt, og vi har derfor definert en litt enklere maskin som er kalt Flink (og som vi kommer tilbake til senere). For å kunne utføre programmer i Flink-kode må vi derfor ha et program som simulerer denne maskinen, altså en Flink-interpret. Vi er altså på mange måter i samme situsjon som for tradisjonell Java (der vår Flink tilsvarer Javas JVM), men merk at begrunnelsene for en slik måte å gjøre det på er forskjellige: For JVM var begrunnelsen at man ønsket å kunne kjøre alle kompilerte Java-programmer på alle maskiner (portabilitet), mens vår begrunnelse for å definere maskinen Flink i INF2100 er å få en enkel nok maskin. 1.4 Språkene i oppgaven I løpet av dette prosjektet må vi forholde oss til flere språk. 1.4.1 Programmeringsspråket Minila Det å lage en kompilator for for eksempel Java ville altså fullstendig sprenge rammen på ti studiepoeng. I stedet er det laget et språk spesielt for dette kurset med tanke på at det skal være overkommelig å oversette. Dette språket er kalt Minila (uttales med trykk på første stavelse utfra betydningen «minilanguage»). Selv om dette språket er enkelt, er det lagt vekt på at man skal kunne uttrykke seg rimelig fritt i det, og at «avstanden» opp til mer realistiske programmeringsspråk ikke skal virke uoverkommelig. Språket Minila blir beskrevet i detalj i kapittel 3 på side 17. 1.4.2 Datamaskinen Flink og dens maskinspråk Det en kompilator oversetter til, er altså vanligvis maskinspråket på den aktuelle maskin. Å forstå hovedideene for maskinspråket, for eksempel for en PowerPC- eller en Pentium-maskin, ville ikke være veldig problematisk, men å bruke det som språk å oversette Minila til, ville trekke med seg så mange detaljer at det ville bli uoverkommelig i et opplegg som dette. For å få et språk å oversette til som er enkelt nok, og for samtidig å beholde et rimelig nært forhold til vanlige kompilatorer, vil vi altså her definere en egen forenklet datamaskin. Den vil ha en del typiske trekk fra vanlige datamaskiner, og vil gi en ganske god følelse for hvordan en datamaskin og dens maskinspråk vanligvis er utformet. En del detaljer er imidlertid fjernet. Side 5

KAPITTEL 1 INNLEDNING Denne maskinen, med sitt maskinspråk og sine maskininstruksjoner er kalt Flink. Flink er en meget enkel maskin og kan for eksempel bare regne med heltall. Maskinen Flink er beskrevet i kapittel 4 på side 29. Det finnes altså ingen maskin som direkte det vil si ved hjelp av elektronikk kan utføre maskinspråket til Flink. At en slik maskin finnes er imidlertid den tenkte forutsetningen for den kompilatoren vi skal lage. For at denne forutsetningen skal være så nær som mulig oppfylt er det laget ferdig et program som simulerer maskinen Flink, både med hensyn til indre arkitektur og med hensyn til evne til å utføre maskininstruksjoner. Dette programmet starter med å lese et Flink-program fra en angitt fil. På filen må Flink-programmet da ligge i et spesielt format som kalles Flok. Slik vi framstiller det her er alt som lagres i Flink-maskinen tall. Dette kan gi næring til den vrangforestilling at datamaskiner dypest sett bare kan behandle tall, så la oss derfor straks se på hva som er tilfellet i en virkelig maskin. Den elementære lagringsenheten her kan bare ha to tilstander, som gjerne er kalt av og på eller 0 og 1. En slik lagringsenhet kalles gjerne et bit (som er en forkortelse for binary digit), og sekvenser av slike bit kan brukes like godt til å representere bokstaver som til å representere tall, bilder, eller hva annet man måtte ha behov for ved bare å bli enig om en passelig koding. Bit grupperes gjerne åtte og åtte til såkalte byte og en datamaskin kan typisk inneholde flere hundre millioner slike byte i sitt interne lager. I det eksterne lager (vanligvis et platelager) kan det tilsvarende være plass til størrelsesorden hundre milliarder byte. Når vi her lar lagerenhetene i Flink inneholde tall, er det fordi det å bruke bit ville bli for komplisert, og fordi tall gir riktige assosiasjoner i hvertfall et stykke på vei. Tall er også greie å behandle i Java og andre programmeringsspråk. 1.4.3 Assembleren Flass Vanligvis når man programmerer en datamaskin, beskriver man det man vil ha gjort i et passelig høynivå programmeringsspråk. En kompilator sørger så, slik som beskrevet over, for å produsere en tilsvarende sekvens av maskininstruksjoner og å legge denne ut på en angitt fil. Denne kan så senere lastes inn i maskinen og utføres. Vår Minila-kompilator skal altså gjøre nettopp dette. Man har imidlertid også av og til behov for å skrive programmer (eller deler av programmer) direkte som en sekvens av maskininstruksjoner. Dette behovet oppstår vanligvis fordi man vil ha programmet spesielt effektivt, eller man ønsker å gjøre noe som høynivåspråket ikke tillater. Vi skal også programmere litt direkte i Flinks maskinspråk i dette kurset, men det er bare for å lære Flinkmaskinen ordentlig å kjenne. Når man skal programmere direkte i maskininstruksjoner, er det svært tungt å bruke tallkoder hele tiden, slik det fremtrer i Flok-koden, og så godt som alle maskiner har derfor for hver av maskinens instruksjoner laget en tekstkode som er lettere å huske enn et tall. For eksempel kan man bruke «ADDI» for en instruksjon som legger sammen heltall i stedet for til dømes tallet 5, som kan være instruksjonens egentlige kode. Man lager så et enkelt program som oversetter sekvenser av slike tekstlige instruksjoner til utførbar maskinkodeform, og slike oversetterprogrammer kalles tradisjonelt assemblere. Det oppsett eller format man må bruke for å angi et maskinprogram til assembleren, kalles gjerne assemblerspråket. Side 6

1.4 SPRÅKENE I OPPGAVEN Minila-program call P with a; INCI var * Flass-kode Kompilator Flass-assembler Flok-kode 2 5 17 10 Flink-simulator Figur 1.1: Sammenhengen mellom Minila, Flass, Flok og Flink Assembler-språket for Flink kalles Flass som står for «Flink assemblerspråk». Det er skrevet ferdig en assembler for dette språket, og denne ligger på Ifis datamaskine. Flass-assembleren tar som input en fil med Flass-kode og oversetter denne til Flink-instruksjoner som blir lagt ut på en angitt fil i Flokformat. For treningens skyld skal vi i kurset skrive en del programmer i Flass. Det nøyaktige formatet for assembler-språket er beskrevet i kapittel 5 på side 37. Flass-assembleren er altså bare skrevet for lettere å kunne sette seg inn i Flinks virkemåte og for å kunne trene seg i bruk av den. Den vil ikke inngå som del av den ferdige kompilator som skal skrives. Forholdet mellom Minila-kompilatoren, Flass-assembleren, Flok-koden og den utførende Flinkmaskinen kan beskrives ved figur 1.1. Både et Minila-program, et Flass-program og et Flok-program er altså et tekstlig program som ligger på en fil. Slike filer kan leses av henholdsvis Minila-kompilatoren, Flass-assembleren og maskinen Flink. De to førstnevnte er programmer som begge er oversettere som produserer Flok-kode, mens den sistnevnte er en simulator av en tenkt maskin. Flass-assembleren er tegnet litt mindre enn Minila-kompilatoren for å markere at den oversetter en «kortere» avstand, altså at et Flass-program ligger «nærmere» maskinkode enn for eksempel Minila (eller Java) gjør. Ja, et Flassprogram er jo bare en tekstlig form for å angi instruksjon for instruksjon til Flink på, mens Minila-kompilatoren jo må «finne på» en fornuftig sekvens av Flink-instruksjoner som gjør det som er foreskrevet i Minila-programmet. Man sier derfor gjerne at Minila (og Java) er høynivåspråk mens for eksempel Flasskode er et lavnivåspråk. Det er selvfølgelig her snakk om en kontinuerlig skala og Java er derfor vesentlig «høyere» enn Minila. Side 7

KAPITTEL 1 INNLEDNING 1.4.4 Oversikt over de ulike språkene i oppgaven Det blir i begynnelsen mange programmeringsspråk å holde orden på før man blir kjent med dem og hvordan de forholder seg til hverandre. Det er fem språk med i bildet: 1) Java, som Minila-kompilatoren skal skrives i. 2) Minila, som kompilatoren skal oversette fra. 3) Flinks maskinspråk, som kompilatoren skal oversette til. 4) Flok-format, som er et passelig tallformat for å lagre maskinprogrammer for Flink på fil. 5) Flass-kode er en tekstlig form for maskin-instruksjoner til Flinkmaskinen. Flass-assembleren kan oversette Flass-koden til Flok-kode. I tillegg ligger altså maskinkoden til de aktuelle maskinene (som Pentium eller PowerPC) under hele tiden, i og med at det er denne alt oversettes til før noe i det hele tatt kan utføres på disse maskinene. Hold tunga rett i munnen, og tenk litt på dette hver dag så føler du deg snart hjemme i det hele! 1.5 Oppgaven og dens tre deler Oppgaven skal løses i tre skritt, hvor alle er obligatoriske oppgaver. Som nevnt kan det ut over dette komme krav om for eksempel verktøybruk eller levering av skriftlige arbeider, men også dette vil i så fall bli annonsert i god tid. Hele programmet kan grovt regnet bli på fra to til tre tusen Java-linjer, alt avhengig av hvor tett man skriver. Erfaringsmessig er det Del-1 som innebærer mest arbeid. Vi gir her en rask oversikt over hva de tre delene vil inneholde, men vi kommer fyldig tilbake til hver av dem på forelesningene og i senere kapitler. 1.5.1 Del 0 Side 8 Første skritt, del 0, består i å få på plass oppstartingen av kompilatoren, og å få programmert hvordan kompilatoren skal få tak i navnet på programfila og en del andre opplysninger fra brukeren. I tillegg skal programfila åpnes, og man skal få ferdig kompilatoren så langt at den kan gjøre en del innledende arbeid med det Minila-programmet som der ligger. Dette skal bestå i å fjerne kommentarer fra programmet, og å sjekke at den gjenstående teksten består av en veldefinert sekvens av såkalte symboler. Symbolene er de enhetene programmet er bygget opp av, så som navn, tall, nøkkelord, +, >=, := og alle de andre tegn og tegnkombinasjoner som har en bestemt betydning i Minilaspråket. Denne «renskårene» sekvensen av symboler vil være det grunnlaget som resten av kompilatoren (del 1 og del 2) skal bruke til å arbeide videre med programmet. Mye av programmet til del 0 vil være ferdig laget eller skissert, og dette vil kunne hentes på angitt sted.

1.6 PROGRAMMERING AV LISTER, TRÆR ETC 1.5.2 Del 1 Del 1 vil ta imot den symbolsekvensen som blir produsert av del 0, og det sentrale arbeidet her vil være å sjekke at denne sekvensen har den formen et riktig Minila-program skal ha (altså, at den følger syntaksen til Minila). Videre skal del 1 sjekke slike ting som at alle prosedyrer og variable er deklarert, osv. Om alt er i orden, skal Del 1 bygge opp en trestruktur av objekter som direkte representerer det aktuelle Minila-programmet, altså hvordan det er satt sammen av uttrykk inne i setninger inne i prosedyrer osv. Denne trestrukturen skal så leveres videre til del 2 som grunnlag for generering av Flink-kode. 1.5.3 Del 2 I del-2 skal man gjøre selve oversettelsen til Flink-kode ved å ta utgangspunkt i den trestrukturen som del 1 produserte for det aktuelle Minila-programmet. Koden skal legges på en fil angitt av brukeren i såkalt Flok-format. I kapittel 6 på side 41 er det angitt hvilke sekvenser av Flink-instruksjoner hver enkelt Minila-konstruksjon skal oversettes til, og det er viktig å merke seg at disse skjemaene skal følges (selv om det i enkelte tilfeller er mulig å produsere lurere Flink-kode; dette skal vi evntuelt se på i noen gruppeoppgaver). 1.6 Programmering av lister, trær etc Noe av hensikten med INF2100 er at man i størst mulig grad skal få en «hands on»-følelse med alle deler av programmeringen ned gjennom alle nivåer. Det er derfor et krav at gruppene selv programmerer all håndtering av lister og trær, og ikke bruker ferdiglagede bibliotekspakker og slikt til det. For de som nettopp har tatt introduksjonskursene, kan dette kanskje være en utfordring, men vi skal bruke noe tid på gruppene til å se på dette, og ut fra eksempler, oppgaver, etc burde det da gå greit. 1.7 Krav til samarbeid og gruppetilhørighet Normalt er det meningen at to personer skal samarbeide om å løse oppgaven. De som samarbeider bør være fra samme øvelsesgruppe på kurset. Man bør tidlig begynne å orientere seg for å finne én på gruppen å samarbeide med. Det er også lov å løse oppgaven alene, men dette vil selvfølgelig gi mer arbeid. Om man har en del programmeringserfaring, kan imidlertid dette være et overkommelig alternativ. Hvis man får samarbeidsproblemer (som at den andre «har meldt seg ut» eller «har tatt all kontroll»), si fra i tide til gruppelærer eller kursledelse så kan vi se om vi kan hjelpe dere å komme over «krisen». Det har skjedd før. Når det gjelder gruppetilhørighet, godkjenning, etc gjelder følgende: Man må forbli på den øvelsesgruppa man havnet i ved påmeldingen i den forstand at all innlevering må foregå til gruppelæreren på denne gruppa. Side 9

KAPITTEL 1 INNLEDNING Dersom to fra forskjellige grupper får tillatelse til å arbeide sammen skal all levering, godkjenning etc foregå på øvelsesgruppa til den av studentene som har det etternavnet som kommer først i alfabetisk ordning. Bortsett fra levering og godkjennelse kan man gå på hvilken gruppe man vil for å delta i oppgaveløsing, få veiledning, stille spørsmål, etc. Dersom det da skulle bli fysiske plassproblemer på visse grupper, vil imidlertid gruppelæreren her be noen som ikke opprinnelig hører til på gruppa om å finne andre grupper å gå på. 1.8 Kontroll av innlevert arbeid For å ha en kontroll på at hvert arbeidslag har programmert og testet ut programmene på egenhånd, og at begge medlemmene har vært med i arbeidet, må studentene være forberedt på at gruppelæreren eller kursledelsen forlanger at studenter som har arbeidet sammen skal kunne redegjøre for oppgitte deler av den kompilatoren de har skrevet. Med litt støtte og hint skal de for eksempel kunne gjenskape deler av selve programmet på en tavle. Slik kontroll vil bli foretatt på stikkprøvebasis samt i noen tilfeller der gruppelæreren har sett lite til studentene og dermed ikke har hatt kontroll underveis med studentenes arbeid. Dessverre har vi tidligere avslørt fusk; derfor ser vi det nødvendig å holde slike overhøringer spesielt på slutten av kurset. Dette er altså ingen egentlig eksamen, bare en sjekk på at dere har gjort arbeidet selv. Noe ekstra arbeid for dem som blir innkalt blir det heller ikke. Når dere har programmert og testet ut programmet kan dere kompilatoren deres forlengs, baklengs og med bind for øynene. Merk at ingen godkjenning av enkeltdeler er endelig før den avsluttende runde med slik muntlig kontroll, og denne blir antakeligvis holdt en gang rundt midten av november. Side 10 1.9 Delta på gruppene Ellers vil vi oppfordre studentene til å være aktive på gruppene. Oppgavene som blir gjennomgått er stort sett meget relevante for skriving av Minilakompilatoren. Om man tar en liten titt på oppgavene før gruppetimene vil man antageligvis få svært mye mer ut av gjennomgåelsen. Selv om man ikke har forberedt seg er det imidlertid helt lov på gruppa å komme med et uartikulert: «Jeg forstår... ikke hva dette har med saken å gjøre!» Antakeligvis føler da flere det på samme måten, så du gjør gruppa en tjeneste. Og om man synes man har en aha-opplevelse så er det fin støtte både for seg selv og andre om du sier: «Aha, det er altså... som er poenget! Stemmer det?»

1.9 DELTA PÅ GRUPPENE Siden det er mange nye begreper å komme inn i, er det viktig å begynne å jobbe med dem så tidlig som mulig i semesteret. Ved så å ta det fram i hodet og oppfriske det noen ganger, vil det neppe ta lang tid før begrepene begynner å komme på plass. Kompendiet sier ganske mye om hvordan oppgaven skal løses, men alle opplysninger om hver programbit står ikke nødvendigvis samlet på ett sted. Til sist et råd fra tidligere studenter: Start i tide! Side 11

Side 12

Kapittel 2 Slik brukeren ser Minila-kompilatoren Dette kapitlet gir en oversikt over hvordan Minila-kompilatoren skal virke sett fra brukerens side, altså for eksempel hvordan den skal gi feilmeldinger, lage listinger med forskjellige testutskrifter etc. Når kompilatoren startes opp, skal man gi med en del opplysninger, blant annet filnavnet til fila der Minila-programmet ligger. Hvordan dette skal gjøres, vil være bygget inn i den programskissen dere får å starte med og er forklart i forbindelse med den. En del av de tingene som er beskrevet i dette kapittelet kan være vonde å forstå uten å ha lest senere kapitler, og dette gjelder spesielt det som står om testutskriftene og om Flok-formatet. Vi kommer fyldig tilbake til dette siden. 2.1 Kompilatorens reaksjon på feil Ved siden av hvor effektiv maskinkode den produserer, vurderes kvaliteten av en kompilator svært mye ut fra dens evne til å gjøre fornuftige ting når det er feil i programmet. Grovt sett kan man ha fire holdninger til hvordan kompilatoren skal håndtere denne situasjonen (og dette gjelder ikke bare kompilatorer, men generelt forholdet mellom et program og de data det skal lese inn). 1) Kompilatoren forutsetter at programmet er korrekt, og tar ikke noe ansvar for hvilke forunderlige ting som kan skje om det likevel finnes feil. 2) Kompilatoren tester hele tiden at det programmet den leser er korrekt, men om den finner en feil gir den bare en melding om dette, og avslutter kompileringen. 3) Kompilatoren tester hele tiden at programmet er korrekt, og om den finner en feil rapporterer den denne, men fortsetter å analysere programmet videre med tanke på å finne flere feil. 4) I tillegg til det som gjøres i punkt nr 3, rapporterer den ikke bare feil, men forsøker også å finne ut hva som «egentlig» var ment og arbeider videre ut fra det. Side 13

KAPITTEL 2 SLIK BRUKEREN SER MINILA-KOMPILATOREN Strategi nr 4 kan virke fristende. Om vi finner en udeklarert variabel, kunne vi for eksempel undersøke om det finnes et deklarert navn som er nesten likt det udeklarerte og i så fall bare anta at dette var en trykkfeil. Slike ting kan imidlertid føre til mange forunderlige og uforutsette effekter og kan i uheldige tilfelle komme til å skjule ting som er feil. Så og si alle kompilatorer arbeider derfor etter den tredje strategien, og kvalitetsforskjellene går stort sett på hvor gode feilmeldinger kompilatoren gir, og hvor fort kompilatoren klarer å ta opp tråden igjen etter en feil. Det å få kompilatoren til å ta opp tråden igjen etter alle mulige slags feil i programmet, viser seg imidlertid å være ganske krevende. Om man ikke er meget omhyggelig, vil dette grumse til strukturen i kompilatorprogrammet ganske kraftig, og det vil i alle tilfelle øke størrelsen på kompilatoren betraktelig. Det å legge oss på denne filosofien ville derfor bli for krevende innenfor den rammen dette kurset har. En annen erfaring er at dagens kompilatorer stort sett er så raske at brukerne aldri ser lenger enn på den første feilen som rapporteres. Det å bruke den første filosofien er helt ubrukelig. Da må man regne med at kompilatoren «dør» uten et ord (eller med en helt irrelevant feilmelding) når det er feil i Minila-programmet. Vi skal derfor legge oss på en mellomløsning, nemlig nummer 2 i lista over. Det vil altså si at vi skal stoppe kompileringen med en fornuftig melding så fort vi finner en feil uten å gjøre noe forsøk på å lese videre i programmet. 2.2 Feilmeldinger og listing med testutskrifter Vår kompilator skal, som de fleste slike systemer, kunne gi to typer utskrift: Den ene typen skal alltid gis, og den skal komme på skjermen og være nokså kort. Den andre typen skal bare gis på forespørsel, den skal være mer fyldig, og den skal legges på en angitt listing-fil (slik at man etterpå kan se på den eller skrive den ut). På skjermen skal det stort sett bare komme korte kommentarer om at kompileringen er ferdig eller eventuelt at det er funnet en feil i Minilaprogrammet og hva den består i. Hovedhensikten med listingen er å kunne få ut såkalte testutskrifter, som er forskjellige typer rapporter om hva som foregår inne i kompilatoren. Det skal bygges inn fem typer slike testutskrifter (kalt henholdsvis S, A, P, K og I) og brukeren må i hver kompilering angi hvilke som ønskes. Dersom brukeren ønsker en listing-fil så skal denne for det første inneholde en kopi av selve Minila-programmet der hver linje er skrevet ut med linjenummer foran. Hver linje skal skrives ut så fort kompilatoren starter behandlingen av den og andre testutskrifter for den linja vil da følge bak dette. Dermed blir den grei å lese når man arbeider med uttesting. Om det oppdages feil i Minila-programmet, skal feilmeldingen skrives ut både på skjermen og på en eventuell listing. Den skal skrives ut sammen med den linjen der feilen er funnet, og man skal så umiddelbart avslutte kompileringen. Feilutskriften skal komme på egen linje og starte med: «*** FEIL:». Selve programlinjen skal skrives ut med linjenummeret foran, for eksempel slik: Side 14

2.3 PRODUKSJON AV FLOK-KODE 123 : if x < y ten *** FEIL : Savner "then", "ten" funnet i stedet *** 2.3 Produksjon av Flok-kode Det maskin-programmet som kompilatoren lager utfra det aktuelle Minilaprogrammet, skal legges ut på en fil i såkalt Flok-format (som tidligere nevnt er en sekvens av tall). Detaljene i Flok-formatet er diskutert i kapittel 4.3 på side 34. Hvilke maskininstruksjoner som skal lages for de forskjellige Minila-setninger er beskrevet i kapittel 6 på side 41. 2.4 Testutskrifter fra kompilatoren Mye av arbeidet med å få ferdig kompilatoren vil erfaringsmessig bestå i å teste ut de programmene man selv skriver. Brukergrensesnittet skal derfor også gi tilgang til funksjoner som er laget spesielt for å lette denne uttestingen, og det er nettopp her de testutskriftene som er omtalt tidligere kommer inn. Disse har altså ikke interesse for vanlige brukere av kompilatoren, og om man skulle lage en «produksjonsutgave» av kompilatoren, vil det være naturlig å fjerne muligheten til slik utskrift. Men for den som arbeider med å lage kompilatoren, er slike testutskrifter fra kompilatorens indre liv av stor verdi. Ved å se på disse kan man følge med hvor langt kompilatoren er kommet med oversettelsen og hvilken rekkefølge ting blir gjort i. På den måten kan man lettere sjekke ut at ting fungerer som de skal, og når og hvorfor det eventuelt feiler. Hvis man ikke har slike testutskrifter å støtte seg på, kan dette være ganske vanskelig. For å kunne styre hvilke testutskrifter som skal gis, må kompilatoren få vite dette av brukeren. Dette skal angis i form av opsjoner som «S A K» og som da skulle bety at man ønsker disse tre typer testutskrift. Side 15

Side 16

Kapittel 3 Språket Minila 3.1 Minila Minila er et programmeringsspråk som er laget nettopp med tanke på denne prosjektoppgaven. Det er forsøkt utformet slik at det er overkommelig å kompilere, samtidig som det skal gi mange av de problemstillinger man står overfor ved kompilering av virkelige programmeringsspråk. Det er også et Endringer i Minila rimelig utbygd språk i den forstand at man innenfor visse rammer kan skrive nokså strukturerte programmer. En målsetning ved utformingen av språket har også vært at man skal se noen alternativer til de syntaktiske løsninger man er vant til fra andre språk som Java eller C. I figur 3.3 3.11 angir vi syntaksen for Minila som såkalte syntaksdiagrammer (også kalt «jernbanediagrammer»). Idéen her er at enhver måte å spasere gjennom disse diagrammene på (i pilenes retning) gir et syntaktisk riktig program. De firkantede boksene skal erstattes av et diagram man finner et annet sted, mens de runde boksene angir direkte hvilket symbol som her skal komme i programmet. Tre av de firkantede boksene er ikke angitt videre, nemlig Navn, Tall og Et tegn. Formen på et Navn er som i de fleste programmeringsspråk, nemlig en bokstav etterfulgt av null eller flere bokstaver eller siffer. Store og små utgaver av samme bokstav skal regnes som forskjellige når navn sammenliknes. Noen bokstavsekvenser er ulovlig å bruke som navn siden de er såkalte nøkkelord (reserverte ord) som har spesiell betydning i språket. Disse skrives alltid med små bokstaver. I tabell 3.1 på side 25 finnes en liste over alle Minilas nøkkelord. Formen på et Tall er rett og slett en sekvens av desimale siffere, og tallet må ikke ha mer enn 9 siffere (for å få en enkel begrensning, slik at tallet kan gå inn i fire byter i standard representasjon). Det er verdt å merke seg at tallkonstanter ikke kan ha fortegn. Et Tegn er ett tegn omgitt av enkle anførselsestegn, som x. 1 Tegnsettet er ISO 8859-1 (også kjent som Latin-1); se figur 3.2 på side 19. fra i fjor er nevnt i slike små notater i margen. 1 Litt spesielt er at anførselstegnet selv kan angis som. Side 17

KAPITTEL 3 SPRÅKET MINILA prog Alle programmer starter med dette nøkkelordet Prosedyre som beregner fak=1*2*... *n proc fakultet in n out fak; var i; begproc Her starter setningene i prosedyren fak:=1; i:=1; while i <= n do fak:= fak*i; i:= i+1; endwhile; Både While og If setningen avsluttes med nøkkelord endproc; var farr[12]; proc skrivfak in n; Skriver ut farr[1],...,farr[n] mm. var i; begproc i:=1; while i <= n do outint(5) i; "outint" er nøkkelord i Minila outint(10) farr[i]; 10 er feltbredden "outline" skriver ut linjebufferen: outline; i:=i+1; endwhile; endproc; var grense,i; begprog Her starter setningene i selve programmet grense := inint; "inint" er nøkkelord i Minila if grense > 12 then grense:=12; Setninger avsluttes med semikolon else if grense < 0 then grense:=0; endif; endif; i:=1; while i <= grense do Kaller prosedyren fakultet call fakultet with i into farr[i]; i:=i+1; endwhile; call skrivfak with grense; endprog Figur 3.1: Eksempel på Minila-kode Side 18