Syntaksanalyse Skanner (repetisjon) Parsering top-down bottom-up LL(1)-parsering Recursive descent Forutsetninger Ark 1 av 26 Forelesning 15.10.2001
Syntaksanalyse En parser er et program som analyserer en tekst i henhold til en syntaks. Alle kompilatorer og interpreter inneholder en parser. Skanner Det er vanlig at en parser har en «preprosessor» som kalles en skanner: fil Skanner tokens Parser Den setter sammen tegn til symboler (ofte kalt tokens), for eksempel BEGIN begin IDENT OutText LPAR ( TEXT "Hallo" RPAR ) END end Forelesning 15.10.2001 Ark 2 av 26
Parsering Parsering vil si å sjekke at en setning (eller et program) er syntaktisk riktig, det vil si å konstruere det tilhørende syntakstreet. (I praksis bryr vi oss gjerne ikke om å faktisk konstruere syntakstreet, vi nøyer oss med å vite at vi kunne laget et hvis vi hadde hatt behov for det.) Generelt ønsker vi å kunne konstruere treet ved å lese setningen en gang, fra venstre mot høyre. Eksempelgrammatikk: uttrykk uttrykk + term uttrykk term term term *navn term navn Ut fra denne grammatikken skal vi se på parsering av setningen: navn * navn + navn Forelesning 15.10.2001 Ark 3 av 26
Top-down parsering Her konstrueres treet ovenfra og ned, det vil si at vi starter med startsymbolet i roten, og så forsøker å avlede den aktuelle setningen ut fra dette: UTTRYKK navn * navn + navn Forelesning 15.10.2001 Ark 4 av 26
Bottom-up parsering Her konstruerer vi treet nedenfra og opp. Vi starter da med å finne noe i setningen som tilsvarer høyresiden i en produksjon, og reduserer denne delsetningen til det tilhørende metasymbolet. Målet er da å redusere seg tilbake til startsymbolet: UTTRYKK navn * navn + navn Forelesning 15.10.2001 Ark 5 av 26
LL(1)-parsering LL(1)-parsering er en top-down strategi der vi foretar en venstreavledning fra startsymbolet. Recursive descent Til hvert metasymbol svarer en metode. Metoden tar seg av det ene metasymbolet, men kan kalle andre metoder. Når metoden kalles, skal skanneren inneholde første symbol i den aktuelle produksjonen (for syntaktisk korrekt setning). Når metoden er ferdig, skal skanneren inneholde første symbol etter den leste teksten. Forelesning 15.10.2001 Ark 6 av 26
En grammatikk for L program setningsliste setningsliste setning + setning tilordning inputsetning outputsetning tilordning variabel = variabel operator operand operator + - operand variabel nummer inputsetning? variabel outputsetning! variabel variabel v siffer siffer 0 1 2 3 4 5 6 7 8 9 nummer siffer + Forelesning 15.10.2001 Ark 7 av 26
Syntaksanalyse av L Siden vi kommer til å sjekke L-program tegn for tegn, kan vi bruke en veldig enkel skanner. Hvert kall på read() legger et nytt tegn i c. import java.io.*; class Scanner { static char symbol = ; static boolean eof = false; static FileReader fil; static void read() { int c; try { do { c = fil.read(); if (c < 0) throw new IOException(); symbol = (char)c; while (Character.isWhitespace(symbol)); catch (IOException e) { eof = true; symbol = ; Scanner(FileReader f) { fil = f; Forelesning 15.10.2001 Ark 8 av 26
Selve parseren kan da se slik ut: import java.io.*; class Lparser { static Scanner s; public static void main(string[] args) { // Initialisering av skanneren: try { s = new Scanner( new FileReader(new File(args[0]))); s.read(); catch (FileNotFoundException e) { System.out.println("Kan ikke lese " + args[0]); System.exit(1); // "program" er startsymbolet i L-grammatikken program();... Forelesning 15.10.2001 Ark 9 av 26
Recursive descent for L For et metasymbol med bare en mulig produksjon (og skrevet i klassisk BNF), er det lett å lage den tilhørende metoden: For hvert grunnsymbol i høyresiden: Sjekk at symbolet i skanneren er lik dette grunnsymbolet. For hvert metasymbol i høyresiden: Kall dennes metode. Hjelpemetode Vi bruker en hjelpemetode: lessymbol(char c) som sjekker om gjeldende symbol (i s.symbol) er lik c. Leser også inn neste symbol (ved et kall på s.read()). Forelesning 15.10.2001 Ark 10 av 26
program setningsliste static void program() { setningsliste(); if (!s.eof) { System.out.println("Tull etter programslutt!"); System.exit(1); tilordning variabel = variabel operator operand static void tilordning() { variabel(); lessymbol( = ); variabel(); operator(); operand(); Forelesning 15.10.2001 Ark 11 av 26
inputsetning? variabel static void inputsetning() { lessymbol(? ); variabel(); outputsetning! variabel static void outputsetning() { lessymbol(! ); variabel(); variabel v siffer static void variabel() { lessymbol( v ); siffer(); Forelesning 15.10.2001 Ark 12 av 26
Alternative høyresider For et metasymbol med alternative høyresider må vi beregne startmengden til hver produksjon, det vil si hvilke grunnsymboler som kan stå først i en setning avledet fra høyresiden i denne produksjonen. Hvis de ulike produksjonene til et metasymbol har disjunkte startmengder, kan vi sjekke klarsymbolet (det siste symbolet lest av skanneren) for å finne ut hvilken produksjon vi skal velge. Vi trenger da en hjelpemetode til: sjekksymbol(char c) som sjekker om gjeldende symbol (i s.symbol) er lik c (uten å lese inn nytt symbol). Forelesning 15.10.2001 Ark 13 av 26
setning tilordning inputsetning outputsetning static void setning() { if (sjekksymbol( v )) { tilordning(); else if (sjekksymbol(? )) { inputsetning(); else if (sjekksymbol(! )) { outputsetning(); operator + static void operator() { if (sjekksymbol( + )) { lessymbol( + ); else if (sjekksymbol( - )) { lessymbol( - ); Forelesning 15.10.2001 Ark 14 av 26
operand variabel nummer static void operand() { if (sjekksymbol( v )) { variabel(); else{ nummer(); siffer 0 1 2 3 4 5 6 7 8 9 Med litt effektivisering får vi: static void siffer() { if (s.symbol >= 0 && s.symbol <= 9 ) { s.read(); else{ System.out.println("Forventet siffer, fikk " + s.symbol); System.exit(1); Forelesning 15.10.2001 Ark 15 av 26
setningsliste setning + static void setningsliste() { setning(); while (sjekksymbol( v ) sjekksymbol(? ) sjekksymbol(! )) { setning(); nummer siffer + Også denne er litt effektivisert: static void nummer() { siffer(); while (s.symbol >= 0 && s.symbol <= 9 ) { siffer(); Forelesning 15.10.2001 Ark 16 av 26
LL(1)-grammatikker Vi kan ikke lage en recursive descent -parser for alle grammtikker, de må (som vi har sett) oppfylle følgende krav: Grammatikken må være kontekstfri, dvs. kun ha ett metasymbol på venstre side. Under parseringen må vi vite hvilket alternativ vi skal velge; dvs. at mengden av startsymboler (den utvidede startmengden) for hvert alternativ må være disjunkte. En LL(1)-grammatikk er en grammatikk som oppfyller dette, og slik at vi hele tiden kan se på neste symbol (men ikke mer) for å bestemme hvilket alternativ som skal velges. Forelesning 15.10.2001 Ark 17 av 26
Beregning av utvidede startmengder For å beregne de utvidede startmengdene må vi først ha beregnet tre andre mengder: Startmengden til et metasymbol: Mengden av de grunnsymbolene som kan stå først i en (del-)setning avledet fra dette metasymbolet. Etterfølgermengden til et metasymbol: Mengden av de grunnsymboler som kan stå etter metasymbolet på et tidspunkt i avledningen. Meta-til-tom mengden: Mengden av de metasymboler som kan videreavledes til den tomme setning. Forelesning 15.10.2001 Ark 18 av 26
Meta-til-tom mengden Metasymboler som kan produsere den tomme setningen kompliserer ting litt, så det kan være greit å ha beregnet denne først: 1. Initialisering: La meta-til-tom mengden bestå av de metasymboler som har en tom høyreside. 2. Dersom det finnes et metasymbol med en høyreside som bare består av metasymboler som er med i meta-til-tom mengden, skal dette også inkluderes i mengden. 3. Gjenta inntil meta-til-tom mengden ikke øker mer. Forelesning 15.10.2001 Ark 19 av 26
Startmengder For enkelthets skyld sier vi at grunnsymboler har seg selv som startmengde. 1. Initialisering: La startmengden for hvert metasymbol være de grunnsymbolene som står først i en høyreside for dette metasymbolet. 2. Se på en høyreside for et metasymbol M. For hvert (grunneller meta-)symbol x i høyresiden som enten står først, eller bare har metasymboler fra meta-til-tom mengden foran, øk startmengden til M med startmengden til x. 3. Gjenta inntil ingen av startmengdene kan økes mer. Forelesning 15.10.2001 Ark 20 av 26
Etterfølgermengder 1. Initialisering: For hvert metasymbol, la etterfølgermengden være unionen av startmengdene til de (grunn- eller meta-) symbolene som i en eller annen høyreside står enten umiddelbart bak dette metasymbolet, eller bak slik at de mellomliggende symboler er metasymboler i meta-til-tom mengden. 2. Hvis metasymbolet M i en høyreside til metasymbolet N enten står bakerst, eller det bak M bare står metasymboler i meta-til-tom mengden: øk etterfølgermengden til M med etterfølgermengden til N. 3. Gjenta til ingen av etterfølgermengdene kan økes mer. Forelesning 15.10.2001 Ark 21 av 26
Eksempel Følgende eksempelgrammatikk er hentet fra eksamen 1996, oppgave 1a: U U @ U AB + A (U) ε B * ε Oppgave: Beregn og angi meta-til-tom mengden, og start- og etterfølgermengden til hvert av metasymbolene. Metasymbol MTT Start Etterfølger U U A B Forelesning 15.10.2001 Ark 22 av 26
Utvidede startmengder - endelig! For en gitt produksjon består denne mengden av alle symbolene i startmengden til de symbolene i høyresiden som enten står først eller bare har metasymboler i meta-til-tom mengden foran seg. Hvis høyresiden er tom eller bare består av meta-til-tom mengden, inngår også symbolene i etterfølgermengden til produksjonens venstreside. Forelesning 15.10.2001 Ark 23 av 26
Eksempel Vi ser igjen på grammatikken: U U @ U AB + A (U) ε B * ε Oppgave: Avgjør om dette er en LL(1)-grammatikk. Begrunn svaret. Utvidet Produksjon startmengde Disjunkt? U U @ U AB U + A (U) A ε B * B ε Forelesning 15.10.2001 Ark 24 av 26
Hva hvis en grammatikk ikke er LL(1)? Vi skal se på to standard-teknikker for å skrive om en grammatikk til å bli LL(1). Ikke-disjunkte startmengder Ofte vil to alternative startsider kunne begynne på samme måte (ha overlappende utvidede startmengder), men forskjellig avslutning, som i: setning uttrykk + term uttrykk * term Vi kan her innføre et nytt metasymbol som skal stå for den varierende delen: setning uttrykk xsetning xsetning + term * term Forelesning 15.10.2001 Ark 25 av 26
Fjerning av venstrerekursjon uttrykk uttrykk + term term Strategi: 1. Skriv om til utvidet BNF: uttrykk term [[ + term ]] 2. Før tilbake til klassisk BNF, men nå med høyrerekursjon (og eventuelt ekstra metasymboler): uttrykk term xterm xterm ε + term xterm Forelesning 15.10.2001 Ark 26 av 26