/* Specifica lessicale e codice Java per un rudimentale assemblatore. Il metodo principale e' Assembla.main (seguito da nome sorgente e, opzionale, nome destinazione). */ package lt.macchina; import java.io.Reader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import lt.macchina.*; import static lt.macchina.Macchina.*; class AssemblatoreException extends RuntimeException { public AssemblatoreException(String s) { super(s); } } //inutile metterla public (che richiederebbe un file separato): //serve solo alla classe Assembla e all'analizzatore lessicale (visibilita' amichevole) %% %class Assembla %public %line %type String //tipo restituito da metodo di scansione, qui non e' utilizzato ma di default //JFlex restituisce Yytoken, che dovrebbe essere definita (vuota) //in questo modo si evita di doverla definire %{ private static Codice cod; //destinazione del codice public static void main(String[] a) throws IOException { if (a.length > 0) { String source = a[0]; String destination = a.length > 1 ? a[1]: "eseguibile"; cod = new Codice(destination); Assembla scanner = new Assembla(new FileReader(a[0])); try { scanner.yylex(); cod.fineCodice(); System.out.println("Codice macchina trascritto nel file " + destination + "\n"); Macchina.main("-L", destination); //visualizza il codice } catch (AssemblatoreException e) { System.out.println(e.getMessage()); } } else System.err.println("Non e' stato specificato il nome del file sorgente"); } /* I campi e i metodi che seguono sono utilizzati per generare il codice controllando la consistenza di istruzioni e operandi: Il campo address contiene 0 prima della generazione di una nuova istruzione, mentre contiene un valore diverso da 0 dopo che e' stata generato il codice di un'istruzione con operando, ma deve essere ancora generato l'operando (in tal caso address contiene l'indirizzo dell'operando). In ognuno dei tre metodi che seguono (generazione di istruzione senza operando, generazione istruzione con operando, generazione dell'operando), si controlla che il valore di address sia consistente con quanto indicato sopra, in caso contrario viene sollevata un'eccezione */ private int address = 0; //indirizzo a cui scrivere l'operando //0 indica che l'ultima istruzione generata non ha operando // per generare istruzioni senza operando private void generaSenza(int istr) { if (address == 0) cod.genera(istr); else throw new AssemblatoreException("Errore alla riga " + (yyline + 1) + ": istruzione " + yytext() + " anziche' operando"); } // per generare istruzioni con operando (solo l'istruzione!) private void generaCon(int istr) { if (address == 0) address = cod.generaParziale(istr); else throw new AssemblatoreException("Errore alla riga " + (yyline + 1) + ": istruzione " + yytext() + " anziche' operando"); } // per generare l'operando private void generaOp(int valore) { if (address != 0) { cod.completaIstruzione(address, valore); address = 0; } else throw new AssemblatoreException("Errore alla riga " + (yyline + 1) + ": letterale " + yytext() + " anziche' istruzione"); } /* Gestione etichette (stringhe alfabetiche, che non siano istruzioni} Possono precedere un'istruzione, seguite immediatamente da due punti, come "pippo:". Possono essere operandi (che verranno espansi con l'indirizzo corrispondente alla label */ private static class Pair { String nome; int valore; Pair(String s) { nome = s; } Pair(String s, int v) { nome = s; valore = v; } public boolean equals(Object o) { if (o instanceof Pair) { return this.nome.equals(((Pair)o).nome); } return false; } } private ArrayList labelDecl = new ArrayList(); // lista dichiarazioni label private ArrayList labelOcc = new ArrayList(); // lista richiami label //dichiarazione di label private void labelDeclaration(String nome) { if (labelDecl.contains(new Pair(nome))) System.err.println("Errore alla riga " + (yyline + 1) + ": label " + nome + " gia' definita"); else labelDecl.add(new Pair(nome, cod.indirizzoProssimaIstruzione())); } //uso di label come operando private void labelOccurrence(String nome) { labelOcc.add(new Pair(nome, cod.indirizzoProssimaIstruzione() - 1)); address = 0; } //sostituzione labels con indirizzi di codice private void labelExpand() { for (Pair p: labelOcc) { int i = labelDecl.indexOf(new Pair(p.nome)); if (i == -1) System.err.println("Errore: Etichetta " + p.nome + " non dichiarata"); else cod.completaIstruzione(p.valore, labelDecl.get(i).valore); } } %} LETTERALE = [:digit:]+ LABEL = [:letter:]+ FINERIGA = \r | \n | \r\n SPAZIATURA = [ \t\f] | {FINERIGA} %% PUSH {generaCon(PUSH);} "PUSH*" {generaSenza(PUSHIND);} PUSH= {generaCon(PUSHIMM);} POP {generaCon(POP);} "POP*" {generaSenza(POPIND);} POP= {generaSenza(POPIMM);} ADD {generaSenza(ADD);} SUB {generaSenza(SUB);} MUL {generaSenza(MUL);} DIV {generaSenza(DIV);} JUMP {generaCon(JUMP);} JZERO {generaCon(JZERO);} JNZERO {generaCon(JNZERO);} JGTZ {generaCon(JGTZ);} JGEZ {generaCon(JGEZ);} JLTZ {generaCon(JLTZ);} JLEZ {generaCon(JLEZ);} "JUMP*" {generaSenza(JUMPIND);} INPUT {generaSenza(INPUT);} OUTPUT {generaSenza(OUTPUT);} OUTPUTCH {generaSenza(OUTPUTCH);} MOVESP {generaSenza(MOVESP);} PUSHPC {generaSenza(PUSHPC);} PUSHSP {generaSenza(PUSHSP);} PUSHSIZE {generaSenza(PUSHSIZE);} INPUTCH {generaSenza(INPUTCH);} HALT {generaSenza(HALT);} {LETTERALE} {generaOp(new Integer(yytext()));} {LABEL}":" {labelDeclaration(yytext().substring(0, yylength() - 1)); } {LABEL} {labelOccurrence(yytext());} {SPAZIATURA} {} "//".* {} . {throw new AssemblatoreException("Errore alla riga " + (yyline + 1) + ": carattere estraneo: " + yytext());} <> {labelExpand(); return null;}