// Skisse til Del-0, INF2100, h?sten 2005 /* Oppgaven er ? fylle ut de delen som mangler og som er markert med ?//##?. Programmet skal da kunne kj?res med opsjonen -S og skrive ut hvilke symboler et Minila-program inneholder. Eksempel: Minila-programmet demo.min ser slik ut: prog var n; begprog n := 1+ 3; -- Svaret skal bli 4. outint(1) n; outline; endprog Da skal kommandoen java Minila -S demo.min produsere filen demo.list som ser omtrent slik ut: 1: prog 2: var n; ---S: prog 7(OTHER) ---S: var 7(OTHER) ---S: n 4(NAME) 3: begprog ---S: ; 7(OTHER) 4: n := 1+ 3; -- Svaret skal bli 4. ---S: begprog 7(OTHER) ---S: n 4(NAME) ---S: := 7(OTHER) ---S: 1 1(NUMBER) 1 ---S: + 5(AROP) ---S: 3 1(NUMBER) 3 ---S: ; 7(OTHER) 5: outint(1) n; ---S: outint 7(OTHER) ---S: ( 7(OTHER) ---S: 1 1(NUMBER) 1 ---S: ) 7(OTHER) ---S: n 4(NAME) 6: outline; ---S: ; 7(OTHER) ---S: outline 7(OTHER) 7: endprog ---S: ; 7(OTHER) ---S: endprog 7(OTHER) ---S: 8(NONE) */ /* * @author Ole J Tetlie * @author Stein Krogdahl * @author Dag Langmyhr */ // SPESIELLE KOMMENTARER I DENNE SKISSEN: // - De som starter med "//##" angir ting som m? fylles ut i Del-0 // - De som starter med "//# " har ? gj?re med ting til Del-1 og Del-2 // <<<<<<<<<<<<<<<<<<<<<<< Compiler >>>>>>>>>>>>>>>>>>>>>>> import java.io.*; public class Minila { static final String version = "2005-1"; public static void main (String argv[]) { MainResponsible mainResp = new MainResponsible(); mainResp.compile(argv); } } // <<<<<<<<<<<<<<<<<<<<<<< MinilaException >>>>>>>>>>>>>>>>>>>>>>> // Dette brukes for ? f?re kontrollen fra ?mainResp.error(...)? og ut // av programmet (til ?shutdown-kallene? lenger ned). class MinilaException extends RuntimeException {} // <<<<<<<<<<<<<<<<<<<<<< MainResponsible >>>>>>>>>>>>>>>>>>>>>>> class MainResponsible { private LineGenerator lineGen; private CharacterGenerator charGen; private SymbolGenerator symbGen; private TreeGenerator treeGen; private ListingResponsible listResp; //# Pluss noen fler, etter hvert som Del-1 og Del-2 skrives // ****** Hovedprosedyren som driver hele kompileringen *****// public void compile (String argv[]) { // Argumentene er: // argv[0] til argv[argv.size-2] - opsjoner // argv[argv.size-1] - Minila.filen String minilaName = null, minilaBase = null; String options = ""; try { lineGen = new LineGenerator(); charGen = new CharacterGenerator(); symbGen = new SymbolGenerator(); treeGen = new TreeGenerator(); listResp = new ListingResponsible(); for (int i = 0; i < argv.length; ++i) { if (argv[i].equals("-S")) { options += "S"; } //# Her kommer det flere opsjoner: -A, -I, -K, -P else if (argv[i].startsWith("-")) { usage(); } else if (minilaName != null) { usage(); } else { minilaName = minilaBase = argv[i]; if (minilaBase.endsWith(".min")) minilaBase = minilaBase.substring(0, minilaBase.length()-4); } } if (minilaName == null) usage(); //## Ytterligere oppsett System.out.println("Dette er Minila-kompilatoren (versjon "+ Minila.version+")"); System.out.println("Analyse: start"); treeGen.readProgram(); // *Her* gj?res jobben System.out.println(" slutt"); //# I Del-2: Kodegeneratoren skal startes her } catch (MinilaException e) { // Feilmelding er skrevet. G? direkte til shutdown og avslutning. } // Gi alle objektene beskjed om ? avslutte seg selv ordentlig: listResp.shutdown(); lineGen.shutdown(); charGen.shutdown(); symbGen.shutdown(); treeGen.shutdown(); } // Minila-kompilatoren avslutter her. Tilbake til op.sys. // ********* Metode for standard behandling av feil ****** // public void error (String message) { //## F? tak i line og linjenummer fra Tegn-generatoren, og skriv ut //## til skjerm (ved f.eks. "System.err.println(...)") //## Skriv ut p? skjermen: " ***FEIL: --- message --- " //## Be ogs? Listing-ansvarlig ? skrive ut feilmeldingen throw new MinilaException(); // Fanges i metoden "compile" over. } // *** Mulig metode for feilbehandling uten ? stoppe *** // // *** NB! Er ikke i bruk for tiden! *** // public void softError (String message) { //## Gj?r n?yaktig som i ?error?, bortsett fra ?throw?-setningen } // *** Metode for mer tilfeldig testutskrift (skjerm og listing) *** // public void testOut (String message) { System.out.println(" ---TEST: " + message); listResp.testOut(message); } // *** Metode for melding om ukorrekt bruk *** // void usage () { System.out.println("Usage: java Minila [-S] file"); System.exit(1); } } // <<<<<<<<<<<<<<<<<<<<<<< ListingResponsible >>>>>>>>>>>>>>>>>>>>>>> class ListingResponsible { private MainResponsible mainResp; private CharacterGenerator charGen; private PrintWriter file; boolean giveStest = false; //# ... og senere ogs? for de andre: A, P, K, I // **************** Oppstart og avslutning ************** // public void setup (String listFile, String options, MainResponsible mainResp, CharacterGenerator charGen) { this.mainResp = mainResp; this.charGen = charGen; try { file = new PrintWriter(new BufferedWriter(new FileWriter(listFile))); } catch (Exception e) { mainResp.error("F?r ikke ?pnet list-filen med navn " + listFile); } if (options != null) { for (int i = 0; i < options.length(); ++i) { if (options.charAt(i) == 'S' ) giveStest = true; } } } public void shutdown() { if (file != null) { file.close(); } } // **************** Behandling av ny linje ************** // public void startNewLine () { // Kalles fra charGen //## Dersom "file != null", skaff linje og linjenummer (fra charGen) //## og skriv det ut ved "file.println(...)" //## Bruk gjerne "rightFill" under for ? f? det pent } // **************** Behandling av feil ************** // public void error (String message) { // Kalles fra "error" i mainResp //## Dersom det er listing-fil, skriv ut p? denne, omtrent slik: //## *** FEIL: ---- message ---- } // **************** S-test-utskrift ********************** // public void testSout (String symbol, int type, int num) { //## Dersom det er listing-fil og det skal gis S-test-utskrift, //## skriv ut p? en linje f?lgende: //## ---S: -(a) det tekstlige symbolet //## -(b) symboltypen (tall og tekst, se metode under) //## -(c) om symbolet er en konstant: verdien av denne //## Bruk gjerne "rightFill"/"leftFill" under for ? f? det pent } public String symbTypeAsString (int symbType) { switch (symbType) { case SymbolGenerator.NUMBER: return "NUMBER"; case SymbolGenerator.NAME: return "NAME"; case SymbolGenerator.AROP: return "AROP"; case SymbolGenerator.RELOP: return "RELOP"; case SymbolGenerator.OTHER: return "OTHER"; case SymbolGenerator.NONE: return "NONE"; } // M? ha en ?return? her, sier Java-kompilatoren (men utf?relsen // skulle aldri komme hit!): return ("Intern feil i Listing-ansv."); } //# Og senere, tilsvarende avsnitt for de andre typene test-utskrift // ***** Og endelig, metode for mer tilfeldig test-utskrift ****** // public void testOut (String message) { // For ymse behov under uttesting // Kalles fra testOut i mainResp. if (file != null) { file.println(" ---TEST: " + message); } } // ********** Hjelpe-metoder for pen utskrift *********** // // ----- LeftFill i to varianter: for streng og for tall ------ // // ?fieldLength? angir lengde av feltet det skal justeres innen public static String leftFill (int fieldLength, String s) { if (s.length() >= fieldLength) return (s); StringBuffer buf = new StringBuffer(); int pos = 0; while (pos < s.length()) buf.append(s.charAt(pos++)); while (pos < fieldLength) { buf.append(' '); ++pos; } return(buf.toString()); } public static String leftFill (int fieldLength, int i) { return leftFill(fieldLength, Integer.toString(i)); } // ----- rightFill i to varianter: for streng og for tall ------ // public static String rightFill (int fieldLength, String s) { if (s.length() >= fieldLength) return (s); StringBuffer buf = new StringBuffer(); int pos = 0; while (pos < fieldLength-s.length()) { buf.append(' '); ++pos; } while (pos < fieldLength) { buf.append(s.charAt(pos-(fieldLength-s.length()))); ++pos; } return(buf.toString()); } public static String rightFill (int fieldLength, int i) { return rightFill(fieldLength, Integer.toString(i)); } } // <<<<<<<<<<<<<<<<<<<<<<< LineGenerator >>>>>>>>>>>>>>>>>>>>>>> class LineGenerator { private BufferedReader file; private MainResponsible mainResp; // *************** Oppstart og avslutning ****************** // public void setup (MainResponsible main, String filename) { mainResp = main; try { file = new BufferedReader(new FileReader(filename)); } catch (FileNotFoundException e) { mainResp.error("Minila-filen kan ikke ?pnes: " + filename); } } public void shutdown () { if (file != null) { try { file.close(); } catch (Exception e) { mainResp.error("F?r ikke lukket Minila-filen") ; } } } // ******* Hoved-metoden som leverer linje for linje ******* // public String nextLine () { if (file != null) { try { return file.readLine(); // Leverer "null" om end-of-file } catch (IOException e) { mainResp.error("F?r ikke lest neste linje fra Minila-filen"); } } // Kommer hit om ?file == null?: mainResp.error("Filen finnes ikke, eller ikke ?pnet"); // Java-kompilatoren forlanger at vi har med denne (men vi // skulle aldri komme hit): return ("Intern feil i Linje-generator"); } } // <<<<<<<<<<<<<<<<<<<<<<< CharacterGenerator >>>>>>>>>>>>>>>>>>>>>>> class CharacterGenerator { private MainResponsible mainResp; private LineGenerator lineGen; private ListingResponsible listResp; private int lineNumber; private String line; private int position; // **************** Oppstart og avslutning *************** // public void setup (MainResponsible mainResp, LineGenerator lineGen, ListingResponsible listResp) { this.mainResp = mainResp; this.lineGen = lineGen; this.listResp = listResp; // Forutsetter har at ?lineGen.setup(...)? allerede er kalt: line = lineGen.nextLine(); if (line == null) mainResp.error("Minila-filen er tom"); lineNumber = 1; position = 0; listResp.startNewLine(); } public void shutdown () { // Ikke noe ? gj?re } // ************** Levering av linje , linjenummer etc. ********* // public int getLineNumber () { return lineNumber; } public String getLine () { if (line == null) return("*** Kunstig linje: Minila-fil u?pnet eller ferdiglest"); return line; } public int getPosition () { // Angir posisjonen p? linja. (Brukes ikke!) return position; } public boolean moreOnLine () { // Er det mer igjen ? lese p? linjen? if (position >= line.length()) return false; if (line.charAt(position) != '-') return true; if (position+1>>>>>>>>>>>>>>>>>>>>>> class SymbolGenerator { private CharacterGenerator charGen; private ListingResponsible listResp; private MainResponsible mainResp; // *************** Oppstart og avslutning *************** // public void setup (MainResponsible mainResp, CharacterGenerator charGen, ListingResponsible listResp) { this.mainResp = mainResp; this.charGen = charGen; this.listResp = listResp; curr = charGen.nextChar(); } public void shutdown () { // Ikke noe ? gj?re } // *** Detalj-info om symbolet: (a), (b) og (c) i kompendiet *** // private String symbol; // Selve symbolet (a) private int symbolType; // Typen av symbolet (b) private int numberValue; // Verdien av en konstant (c) private String textValue; // Verdien av en tekstkonstant // ------- Navnede konstanter for typen av symbolet ------- // public static final int NUMBER = 1, CHAR = 2, TEXT = 3, NAME = 4, AROP = 5, RELOP = 6, OTHER = 7, NONE = 8; // *********** Variabel som holder sist leste tegn ************ // private char curr; // Etter readSymbol: Inneholder tegnet bak symbolet // ******* Hovedmetoden som leser inn neste symbol ********* // public void readSymbol () { StringBuffer buf = new StringBuffer(16); // Brukes til ? samle sammen til et symbol // (Vi setter av plass til 16 tegn, men lengden ?kes automatisk // ved behov. // De fleste symboler vil alts? ikke beh?ve den kostbare prosessen // det er ? ?ke lengden.) while (Character.isWhitespace(curr)) // Leser forbi blanke og TAB curr = charGen.nextChar(); // ******* Behandler symbol-typer ut fra f?rste tegn: ********** // // ---------------------------------------------------------------- if (Character.isLetter(curr)) { buf.append(curr); curr = charGen.nextChar(); while (Character.isLetterOrDigit(curr)) { buf.append(curr); curr = charGen.nextChar(); } if (IsKeyword(buf)) symbolType = OTHER; else symbolType = NAME; } // ---------------------------------------------------------------- else if (Character.isDigit(curr)) { buf.append(curr); curr = charGen.nextChar(); while (Character.isDigit(curr)) { buf.append(curr); curr = charGen.nextChar(); } if (buf.length() > 9) mainResp.error("For stor konstant"); symbolType = NUMBER; numberValue = Integer.parseInt(buf.toString()); } // ---------------------------------------------------------------- else if (curr == '\'') { //## Ta vare p? en tegnkonstant. //## Husk ? sjekke p? feil. } // ---------------------------------------------------------------- else if (curr == '"') { //## Ta vare p? en tekstkonstant. //## Husk: Den kan ikke g? over flere linjer. } // ---------------------------------------------------------------- else if (curr == ';' || curr == ',' || curr == '(' || curr == ')' || curr == '[' || curr == ']' ) { buf.append(curr); curr = charGen.nextChar(); symbolType = OTHER; } // ---------------------------------------------------------------- else if (curr == ':') { buf.append(curr); curr = charGen.nextChar(); if (curr == '=') { buf.append(curr); curr = charGen.nextChar(); } else mainResp.error("Ulovlig symbol"+buf); symbolType = OTHER; } // ---------------------------------------------------------------- else if (curr == '+' || curr == '-' || curr == '*' || curr == '/') { //## som over symbolType = AROP; } // ---------------------------------------------------------------- else if (curr == '=') { //## Litt mer komplisert //## Husk symbolet ?=/=?. Gi feilmelding! } // ---------------------------------------------------------------- else if (curr == '<') { //## Forholdsvis greit } // ---------------------------------------------------------------- else if (curr == '>') { //## Enda litt greiere } // ---------------------------------------------------------------- else if (curr == '\0') { // 'curr' er "tom": End-of-file. Setter ikke noe ned i buf symbolType = NONE; } // ---------------------------------------------------------------- else { mainResp.error("Ulovlig tegn: '" + curr + "'"); } symbol = buf.toString(); listResp.testSout(symbol, symbolType, numberValue); } // ********** Sp?rre-metoder om innev?rende symbol *********** // public String curSymbol () { return symbol; } public boolean curIs (String str) { // Grei ? ha for likhets-testing return symbol.equals(str); } public boolean curIsName () { return symbolType == NAME; } public boolean curIsConstant () { //## Er det et tall eller et tegn? return true; //## M? rettes! } public int curAsInt () { // Bare meningsfull for konstanter return numberValue; } public boolean curIsArop () { return symbolType == AROP; } public boolean curIsRelop () { return symbolType == RELOP; } public boolean curIsEnd () { return symbolType == NONE; } // ************* Hjelpe-metode til readSymb ****************** // private boolean IsKeyword (StringBuffer buf) { String symb = buf.toString(); return symb.equals("begproc") || symb.equals("begprog") || //## ... og noen flere ... symb.equals("var"); } } // <<<<<<<<<<<<<<<<<<<<<<< TreeGenerator >>>>>>>>>>>>>>>>>>>>>>> class TreeGenerator { //# Forel?pig utgave. Senere: Flere variable og metoder private MainResponsible mainResp; private SymbolGenerator symbGen; private ListingResponsible listResp; //# Senere ogs? peker til roten av treet // **************** Oppstart og avslutning ************** // public void setup (MainResponsible mainResp, SymbolGenerator symbGen, ListingResponsible listResp) { this.mainResp = mainResp; this.symbGen = symbGen; this.listResp = listResp; } public void shutdown () { // Ikke noe ? gj?re } // **************** Hoved-metode ************** // public void readProgram () { //# Forel?pig bare en dummy-utgave symbGen.readSymbol(); while (! symbGen.curIsEnd()) { symbGen.readSymbol(); } } }