package test.antlr.assembler; /*** * Excerpted from "Language Implementation Patterns", * published by The Pragmatic Bookshelf. * Copyrights apply to this code. It may not be used to create training material, * courses, books, articles, and the like. Contact us if you are in doubt. * We make no guarantees that this code is fit for any purpose. * Visit http://www.pragmaticprogrammer.com/titles/tpdsl for more book information. ***/ import org.antlr.runtime.*; import java.util.*; /** Subclass the AssemblerParser to actually implement the necessary * symbol table management and code generation functions. */ public class BytecodeAssembler extends AssemblerParser { public static final int INITIAL_CODE_SIZE = 1024; protected Map<String,Integer> instructionOpcodeMapping = new HashMap<String,Integer>(); protected Map<String, LabelSymbol> labels = // label scope new HashMap<String, LabelSymbol>(); /** All float and string literals have unique int index in constant * pool. We put FunctionSymbols in here too. */ protected List<Object> constPool = new ArrayList<Object>(); protected int ip = 0; // Instruction pointer; used to fill code[] protected byte[] code = new byte[INITIAL_CODE_SIZE]; // code memory protected int dataSize; // set via .globals protected FunctionSymbol mainFunction; /** Create an assembler attached to a lexer and define * the instruction set. */ public BytecodeAssembler(TokenStream lexer, BytecodeDefinition.Instruction[] instructions) { super(lexer); for (int i=1; i<instructions.length; i++) { instructionOpcodeMapping.put(instructions[i].name.toLowerCase(),i); } } public byte[] getMachineCode() { return code; } public int getCodeMemorySize() { return ip; } public int getDataSize() { return dataSize; } /** Return the address associated with label "main:" if defined */ public FunctionSymbol getMainFunction() { return mainFunction; } /** Generate code for an instruction with no operands */ protected void gen(Token instrToken) { String instrName = instrToken.getText(); Integer opcodeI = instructionOpcodeMapping.get(instrName); if ( opcodeI==null ) { System.err.println("line "+instrToken.getLine()+ ": Unknown instruction: "+instrName); return; } int opcode = opcodeI.intValue(); ensureCapacity(ip+1); code[ip++] = (byte)(opcode&0xFF); } /** Generate code for an instruction with one operand */ protected void gen(Token instrToken, Token operandToken) { gen(instrToken); genOperand(operandToken); } protected void gen(Token instrToken, Token oToken1, Token oToken2) { gen(instrToken, oToken1); genOperand(oToken2); } protected void gen(Token instrToken, Token oToken1, Token oToken2, Token oToken3) { gen(instrToken, oToken1, oToken2); genOperand(oToken3); } protected void genOperand(Token operandToken) { String text = operandToken.getText(); int v = 0; switch ( operandToken.getType() ) { // switch on token type case INT : v = Integer.valueOf(text); break; case CHAR : v = Character.valueOf(text.charAt(1)); break; case FLOAT : v = getConstantPoolIndex(Float.valueOf(text)); break; case STRING: v = getConstantPoolIndex(text); break; case ID : v = getLabelAddress(text); break; case FUNC : v = getFunctionIndex(text); break; case REG : v = getRegisterNumber(operandToken); break; } ensureCapacity(ip+4); // expand code array if necessary writeInt(code, ip, v); // write operand to code byte array ip += 4; // we've written four bytes } protected int getConstantPoolIndex(Object o) { if ( constPool.contains(o) ) return constPool.indexOf(o); constPool.add(o); return constPool.size()-1; } public Object[] getConstantPool() { return constPool.toArray(); } protected int getRegisterNumber(Token rtoken) { // convert "rN" -> N String rs = rtoken.getText(); rs = rs.substring(1); return Integer.valueOf(rs); } /** After parser is complete, look for unresolved labels */ protected void checkForUnresolvedReferences() { for (String name : labels.keySet()) { LabelSymbol sym = (LabelSymbol) labels.get(name); if ( !sym.isDefined ) { System.err.println("unresolved reference: "+name); } } } /** Compute the code address of a label */ protected int getLabelAddress(String id) { LabelSymbol sym = (LabelSymbol) labels.get(id); if ( sym==null ) { // assume it's a forward code reference; record opnd address sym = new LabelSymbol(id, ip, true); sym.isDefined = false; labels.put(id, sym); // add to symbol table } else { if ( sym.isForwardRef ) { // address is unknown, must simply add to forward ref list // record where in code memory we should patch later sym.addForwardReference(ip); } else { // all is well; it's defined--just grab address return sym.address; } } return 0; // we don't know the real address yet } protected void defineFunction(Token idToken, int args, int locals) { String name = idToken.getText(); FunctionSymbol f = new FunctionSymbol(name, args, locals, ip); if ( name.equals("main") ) mainFunction = f; // Did someone referred to this function before it was defined? // if so, replace element in constant pool (at same index) if ( constPool.contains(f) ) constPool.set(constPool.indexOf(f), f); else getConstantPoolIndex(f); // save into constant pool } protected int getFunctionIndex(String id) { int i = constPool.indexOf(new FunctionSymbol(id)); if ( i>=0 ) return i; // already in system; return index. // must be a forward function reference // create the constant pool entry; we'll fill in later return getConstantPoolIndex(new FunctionSymbol(id)); } protected void defineDataSize(int n) { dataSize=n; } protected void defineLabel(Token idToken) { String id = idToken.getText(); LabelSymbol sym = (LabelSymbol)labels.get(id); if ( sym==null ) { LabelSymbol csym = new LabelSymbol(id, ip, false); labels.put(id, csym); // add to symbol table } else { if ( sym.isForwardRef ) { // we have found definition of forward sym.isDefined = true; sym.address = ip; sym.resolveForwardReferences(code); } else { // redefinition of symbol System.err.println("line "+idToken.getLine()+ ": redefinition of symbol "+id); } } } protected void ensureCapacity(int index) { if ( index >= code.length ) { // expand int newSize = Math.max(index, code.length) * 2; byte[] bigger = new byte[newSize]; System.arraycopy(code, 0 , bigger, 0, code.length); code = bigger; } } public static int getInt(byte[] memory, int index) { int b1 = memory[index++]&0xFF; // mask off sign-extended bits int b2 = memory[index++]&0xFF; int b3 = memory[index++]&0xFF; int b4 = memory[index++]&0xFF; int word = b1<<(8*3) | b2<<(8*2) | b3<<(8*1) | b4; return word; } /** Write value at index into a byte array highest to lowest byte, * left to right. */ public static void writeInt(byte[] bytes, int index, int value) { bytes[index+0] = (byte)((value>>(8*3))&0xFF); // get highest byte bytes[index+1] = (byte)((value>>(8*2))&0xFF); bytes[index+2] = (byte)((value>>(8*1))&0xFF); bytes[index+3] = (byte)(value&0xFF); } }