Dagens tema: 12 gode råd for en kompilatorskriver Hva skal gjøres med navn? Sjekking av navn Hvordan sjekke navn? Testutskrifter 12 gode råd En kompilator må også sjekke riktig navnebruk: Det må ikke forekomme dobbeltdeklarasjoner Alle navn må være deklarert Navnet må brukes riktig (enkel variabel kontra array kontra prosedyre) Lagring av navn Lagring av navn Hvordan finne et navn? Det enkleste er å lagre navnene i deklarasjonsobjektet: class DeclSeqv { private Decl firstdecl, lastdecl; class VarDecl extends Decl { private boolean isarray, isparam; private int arraylength; I Minila rammet kan vi ha følgende setning: x := a[4] + 1; class Decl { private Decl next; protected String name; class ProcDecl extends Decl { private VarDecl inpar, outpar; private DeclSeqv ds; private Seqv ss; Hvilken x er det snakk om, og hvilken a?
Lokale variable Det enkleste er å sjekke om variabelen er en lokal variabel proc p in a[]; var x; x := a[4] + 1; La analyze få med en parameter som peker til den lokale deklarasjonslisten: void analyze (DeclSeqv localdeclseqv) { Global navn Navn kan imidlertid være deklarert «lenger ute»: proc p1; var b; proc p2; var c; a b c : Hvordan finner globale navn? Det enkleste er å la hvert DeclSeqv objekt ha en peker til sin nærmeste omliggende deklarasjonsliste Skjuling av navn Deklarasjonsrekkefølgen Som i de fleste språk vil en indre deklarasjon skjule en ytre proc p1; a endproc : Husk at navn i Minila først er kjent etter at de er deklarert proc p1; proc p2; a proc p3; a endproc :
Oversikt Testutskrifter Det er lett å gjøre feil når man rammerer noe såpass komplisert som en kompilator Det lureste er å godta dette og heller finne teknikker for å oppdage feilen S utskriftene fra del 0 forteller om analysen er synkronisert med SymbolGenerator, A utskrifter avslører om man gjør riktige valg i jernbanediagrammene Det beste er å la hver analyze gi lyd fra seg P utskrifter sjekker om analysetreet ble riktig ved å skrive det ut etterpå A utskrifter Et veldig enkelt Minila ram var x, z; proc read out a; a := inint; proc write in a; outint(4) a; outline; beg call read into x; z := x; call write with z; end A utskrifter P utskrifter 1: Et veldig enkelt Minila ram 2: 3: A: INN Program 4: var x, z; A: INN DeclSeqv 5: 6: proc read out a; A: INN ProcDecl 7: A: INN DeclSeqv A: UT DeclSeqv 8: a := inint; A: INN Assign A: INN AssignVar A: UT AssignVar A: INN Expression A: INN InIntPart A: UT InIntPart A: UT Expression A: UT Assign 9: A: UT Seqv 10: A: UT ProcDecl 11: proc write in a; A: INN ProcDecl 12: 13: outint(4) a; outline; A: INN DeclSeqv A: UT DeclSeqv A: INN OutInt A: INN Expression A: INN VarPart A: UT VarPart A: UT Expression A: UT OutInt A: INN OutLine A: UT OutLine 14: A: UT Seqv 15: beg A: UT ProcDecl A: UT DeclSeqv 16: call read into x; A: INN Call A: INN AssignVar A: UT AssignVar A: UT Call 17: z := x; A: INN Assign A: INN AssignVar A: UT AssignVar var x ; var z ; proc read out a ; a := inint ; endproc ; proc write in a ; outint( 4 ) a ; outline ; endproc ; beg call read into x ; z := x ; call write with z ; end
Start nå! Råd nr 1: Start nå! Forstå hva du skal gjøre! Råd nr 2: Forstå problemet! Det tar typisk 10 50 timer å rammere Del 1 om man ikke har gjort slikt før Påtrengende spørsmål Det er 21 dager til 7 november Hvor mange timer per dag blir det? Forstå hva du skal gjøre før du begynner å rammere NB! Skriv noen korte kodesnutter i Minila Tegn syntakstrærne deres Studér eksemplet med kalkulatoren i øvelsesoppgavene På dette stadium kan man samarbeide så mye man ønsker! Forstå hva du skal gjøre! Program Start enkelt! Råd nr 3: Start med noe enkelt! DeclSeqv Seqv Ingen bør forvente at de kan skrive all koden og så bare virker den Start med et enkelt rammeringsspråk Decl VarDecl OutText var n; beg outtext "Skriv et tall: "; n := inint; outtext "Det dobbelte er "; outint(1) 2*n; outline; end Assign Expression ExprPart InIntPart OutText OutInt OutLine Expression ExprPart ExprPart ConstPart VarPart Program : beg end og få det til å virke først Utvid etter hvert Sjekk hver utvidelse før du går videre
Start enkelt! class Program { listresptestaout("inn", "Program"); if(! symbgencuris("")) mainresperror("programmet startet ikke med "); symbgenreadsymbol(); if(! symbgencuris("beg")) mainresperror("forventet beg"); symbgenreadsymbol(); if(! symbgencuris("end")) mainresperror("forventet end"); symbgenreadsymbol(); Bruk aktiv feilsøking! Råd nr 4: Ikke sitt og stirr på koden! Når rammet ikke virker: 1 Se på siste versjon av ramkoden 2 Siden du arbeider i små steg er feilen sannsynligvis i de siste linjene du endret 3 Hvis du ikke har funnet feilen i løpet av fem minutter, gå over til aktiv feilsøking listresptestaout("ut","program"); Husk testutskriftene! Råd nr 5: Les testutskriftene! A utskriftene forteller hvilke analyze metoder som kalles Anta at vi har rammet var i; beg i := 3; end Programmet gir en feilmelding i linje 2: ***Feil: Forventet beg Hva er galt? Husk testutskriftene! Svaret kan vi finne i A utskriften: 1: 2: var i; A: INN Program ***Feil: Forventet beg Utskriften skulle ha startet 1: 2: var i; A: INN Program A: INN DeclSeqv 3: beg 4: i := 3; A: UT DeclSeqv A: INN Assign A: INN AssignVar
Husk testutskriftene! P utskriften viser en utskrift av det genererte treet Anta at vi har det samme testrammet var i; beg i := 3; end Hvis P utskriften er var i ; beg end så vet vi at setninglisten ikke settes opp riktig (eller at det er feil i P utskriften) Lag egne testutskrifter Råd nr 6: Lag egne testutskrifter Her er litt (muligens feilaktig) kode fra Seqvanalyze: listresptestaout("inn", "Seqv"); new = null; while (moreences()) { if (symbgencurisname()) new = new Assign(); else if (symbgencuris("call")) new = new Call(); else if (symbgencuris("if")) new = new If(); else if (symbgencuris("intext")) new = new InText(); else if (symbgencuris("outchar")) new = new OutChar(); else if (symbgencuris("outint")) new = new OutInt(); else if (symbgencuris("outline")) new = new OutLine(); else if (symbgencuris("outtext")) new = new OutText(); else if (symbgencuris("while")) new = new While(); newanalyze(localdeclseqv); add(new); if (! symbgencuris(";")) mainresperror("forventet ; etter setning"); symbgenreadsymbol(); listresptestaout("ut", "Seqv"); Lag egne testutskrifter Behold testutskriftene! Råd nr 7: Behold testutskriftene! Anta at vi får melding om null peker i linjen med newanalyze Slikt skal ikke skje Mitt råd er å legge inn noe à la while (moreences()) { Systemoutprintln("Seqvanalyze: " + "symbgencuris er " + symbgencuris()); Når feilen er funnet, bør man la testutskriften forbli i koden Man kan få bruk for den igjen Derimot bør man kunne slå den av eller på: Det mest avanserte er å bruke opsjoner på kommandolinjen: java MinMinila debugssa testmin
Behold testutskriftene! Stol ikke på det du har skrevet! Råd nr 8: Mistro din egen kode! Det fungerer også godt å benytte statusvariable: static boolean debugss_a = true; if (debugss_a) { Systemoutprintln("Seqvanalyze: " + "symbgencuris er " + symbgencuris()); Det er altfor lett å stole på at ens egen kode andre steder er korrekt Løsningen er å legge inn assertions som bare sjekker at alt er som det skal være (som vist her i Ifanalyze): listresptestaout("inn", "If"); assert symbgencuris("if"): "If setning starter ikke med if"; Stol ikke på det du har skrevet! NB! Synkronisér med symbolgeneratoren! Råd nr 9: Sjekk spesielt på readsymbol! Det er lett å kallereadsymbol for ofte eller for sjelden Her er et forsøk på å skrive Ifanalyze: listresptestaout("inn", "If"); Husk å kjøre med java ea Minila for å slå på mekanismen cond = new Condition(); condanalyze(localdeclseqv); if (! symbgencuris("then")) mainresperror("forventet then etter betingelse"); SS = new Seqv(); SSanalyze(localDeclSeqv); if (symbgencuris("else")) { elsess = new Seqv(); elsessanalyze(localdeclseqv); if (! symbgencuris("endif")) mainresperror("forventet endif til slutt i If setn"); listresptestaout("ut", "If"); Hvor skal det kall på readsymbol?
Synkronisér med symbolgeneratoren! Husk kontrakten med SymbolGenerator Her er reglene som analyze metodene må følge: 1 Når man kaller analyze, skal første symbol være lest inn 2 Når man returnerer fra en analyze, skal første symbol etter konstruksjonen være lest Vær spesielt oppmerksom på if tester og løkker Synkronisér med symbolgeneratoren! listresptestaout("inn", "If"); symbgenreadsymbol(); cond = new Condition(); condanalyze(localdeclseqv); if (! symbgencuris("then")) mainresperror("forventet then etter betingelse"); symbgenreadsymbol(); SS = new Seqv(); SSanalyze(localDeclSeqv); if (symbgencuris("else")) { symbgenreadsymbol(); elsess = new Seqv(); elsessanalyze(localdeclseqv); if (! symbgencuris("endif")) mainresperror("forventet endif til slutt i If setn"); symbgenreadsymbol(); listresptestaout("ut", "If"); Ta sikkerhetskopier! Råd nr 10: Ta kopier daglig eller oftere! Hele folket i arbeid! Råd nr 11: Fordel arbeidet! Programmering er mye prøving og feiling Noen ganger må man bare glemme alt man gjorde den siste timen Det finnes systemer for versjonskontroll som man bør lære seg før eller siden I mellomtiden kan man ha nytte av 1 Ta en kopi av Java filen hver gang du starter med å legge inn ny kode 2 Ta uansett en kopi hver dag (om noe som helst er endret) Dere er to om jobben Selv om begge må kjenne til hovedstrukturen, kan man fordele rammeringen Forslag 1 Én tar det som har med deklarasjoner 2 Én tar det som har med setninger Men Snakk ofte sammen Planlegg hvordan dere bruker filene så ikke den ene ødelegger det den andre har gjort
Søk profesjonell hjelp! Råd nr 12: Bruk hjelpemidlene Hva har vi vært innom? Vi har snakket om Spør gruppelærerne! De er tilgjengelige under gruppetimene og svarer på e post til andre tider Orakel Siste uken før oblig fristene vil de sitte på terminalstuen i Abel for å svare på spørsmål Les kompendiet Stoffet er forklart med flere detaljer enn det er mulig på forelesningene Navn lagres i deklarasjonsobjektet analyze får med peker til lokale deklarasjonsliste Hver deklarasjonsliste peker på listen utenfor Testutskriftene er svært nyttige 12 gode råd