package nebula.vm;
/***
* 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 static nebula.vm.BytecodeDefinition.MASK_XX;
import static nebula.vm.BytecodeDefinition.MASK_X_;
import static nebula.vm.BytecodeDefinition.OFFSET_A_;
import static nebula.vm.BytecodeDefinition.OFFSET_B_;
import static nebula.vm.BytecodeDefinition.OFFSET_C_;
import static nebula.vm.BytecodeDefinition.OFFSET_OP;
import static nebula.vm.BytecodeDefinition.OFFSET_X_;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nebula.vm.BytecodeDefinition.Instruction;
import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
/**
* Subclass the AssemblerParser to actually implement the necessary symbol table
* management and code generation functions.
*/
public class AsmCompiler 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> poolLocalK = new ArrayList<>();
protected List<MethodSymbol> functions = new ArrayList<>();
protected List<FieldSymbol> fields = new ArrayList<>();
protected int ip = 0; // Instruction address pointer; used to fill code
protected int[] codeBuffer = new int[INITIAL_CODE_SIZE]; // code memory
// protected int dataSize; // set via .globals
protected MethodSymbol currentFunction;
protected ClassSymbol currentClass;
/**
* Create an assembler attached to a lexer and define the instruction set.
*/
public AsmCompiler(TokenStream lexer, Instruction[] instructions) {
super(lexer);
for (int i = 1; i < instructions.length; i++) {
instructionOpcodeMapping.put(instructions[i].name.toLowerCase(), i);
}
}
public ClassSymbol finished() {
if (currentFunction != null) {
int[] code = new int[ip];
System.arraycopy(codeBuffer, 0, code, 0, ip);
currentFunction.code = code;
}
currentClass.poolLocalK = poolLocalK.toArray();
currentClass.fields = this.fields.toArray(new FieldSymbol[0]);
currentClass.methods = this.functions.toArray(new MethodSymbol[0]);
return currentClass;
}
/** Generate code for an instruction with no operands */
protected void gen(Token instrToken) {
codeBuffer[ip++] = doGenOpcode(instrToken);
}
protected int doGenOpcode(Token instrToken) {
// System.out.println("Gen "+instrToken);
String instrName = instrToken.getText();
Integer opcodeI = instructionOpcodeMapping.get(instrName);
if (opcodeI == null) {
System.err.println("line " + instrToken.getLine() + ": Unknown instruction: " + instrName);
return -1;
}
ensureCapacity(ip + 1);
int op = opcodeI.intValue();
return (op & 0xFF) << OFFSET_OP;
}
/** Generate code for an instruction with one operand */
protected void gen(Token instrToken, Token oTokenAX) {
int op = doGenOpcode(instrToken);
codeBuffer[ip++] = genOperand(oTokenAX, op, OFFSET_A_);
}
protected void gen(Token instrToken, Token oTokenAX, Token oTokenBX) {
int op = doGenOpcode(instrToken);
op = genOperand(oTokenAX, op, OFFSET_A_);
codeBuffer[ip++] = genOperand(oTokenBX, op, OFFSET_B_);
}
protected void gen(Token instrToken, Token oTokenAX, Token oTokenBX, Token oTokenCX) {
int op = doGenOpcode(instrToken);
op = genOperand(oTokenAX, op, OFFSET_A_);
op = genOperand(oTokenBX, op, OFFSET_B_);
codeBuffer[ip++] = genOperand(oTokenCX, op, OFFSET_C_);
}
protected int genOperand(Token operandToken, int op, int offset) {
String text = operandToken.getText();
int v = 0;
switch (operandToken.getType()) { // switch on token type
case CLASS:
v = toLocalConstantPoolIndex(new ClassSymbol(text));
op |= (v & MASK_X_) << offset;
break;
case FIELD:
int i = text.indexOf('.');
ClassSymbol c = new ClassSymbol(text.substring(1, i));
c = (ClassSymbol) poolLocalK.get(toLocalConstantPoolIndex(c));
v = toLocalConstantPoolIndex(new FieldSymbol(c, text.substring(i + 1), BuiltInTypeSymbol.FLEX));
op |= (v & MASK_X_) << offset;
break;
case INT:
v = Integer.valueOf(text);
op |= (v & MASK_XX) << (offset - OFFSET_X_);
break;
case CHAR:
v = Character.valueOf(text.charAt(1));
op |= (v & MASK_XX) << (offset - OFFSET_X_);
break;
case FLOAT:
v = toLocalConstantPoolIndex(Float.valueOf(text));
op |= (v & MASK_X_) << offset;
break;
case STRING:
v = toLocalConstantPoolIndex(text);
op |= (v & MASK_X_) << offset;
break;
case ID:
v = getLabelAddress(text, offset - OFFSET_X_);
op |= (v & MASK_XX) << (offset - OFFSET_X_);
break;
case FUNC:
i = text.indexOf('.');
c = new ClassSymbol(text.substring(1, i));
c = (ClassSymbol) poolLocalK.get(toLocalConstantPoolIndex(c));
v = toLocalConstantPoolIndex(new MethodSymbol(c, text.substring(i + 1), BuiltInTypeSymbol.FLEX));
op |= (v & MASK_X_) << (offset);
break;
case REG:
v = toRegisterNumber(operandToken);
op |= (v & MASK_X_) << offset;
break;
}
return op;
}
protected int toLocalConstantPoolIndex(Object o) {
if (poolLocalK.contains(o))
return poolLocalK.indexOf(o);
poolLocalK.add(o);
return poolLocalK.size() - 1;
}
// public Object[] getConstantPool() {
// return poolLocalK.toArray();
// }
protected int toRegisterNumber(Token rtoken) { // convert "rN" -> N
return Integer.valueOf(rtoken.getText());
}
/** 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, int offset) {
LabelSymbol sym = (LabelSymbol) labels.get(id);
if (sym == null) {
// assume it's a forward code reference; record opnd address
sym = new LabelSymbol(id, ip - 1, offset, 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 - 1, offset);
} else {
// all is well; it's defined--just grab address
return sym.address;
}
}
return 0; // we don't know the real address yet
}
@Override
protected void defineClass(Token idToken) {
this.currentClass = new ClassSymbol(idToken.getText());
toLocalConstantPoolIndex(this.currentClass);
}
@Override
protected void defineField(Token idToken) {
FieldSymbol field = new FieldSymbol(this.currentClass, idToken.getText(), BuiltInTypeSymbol.FLEX);
toLocalConstantPoolIndex(field);
fields.add(field);
}
protected void defineFunction(Token idToken, int args, int locals) {
String name = idToken.getText();
if (currentFunction != null) {
int[] code = new int[ip];
System.arraycopy(codeBuffer, 0, code, 0, ip);
currentFunction.code = code;
}
ip = 0;
currentFunction = new MethodSymbol(currentClass, name, BuiltInTypeSymbol.FLEX, args, locals, codeBuffer);
functions.add(currentFunction);
// 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 (poolLocalK.contains(currentFunction))
poolLocalK.set(poolLocalK.indexOf(currentFunction), currentFunction);
else
toLocalConstantPoolIndex(currentFunction); // save into constant
// pool
}
// protected int getFunctionIndexx(String id) {
// int i = poolLocalK.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 toLocalConstantPoolIndex(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, 0, 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(codeBuffer);
} else {
// redefinition of symbol
System.err.println("line " + idToken.getLine() + ": redefinition of symbol " + id);
}
}
}
protected void ensureCapacity(int index) {
if (index >= codeBuffer.length) { // expand
int newSize = Math.max(index, codeBuffer.length) * 2;
int[] bigger = new int[newSize];
System.arraycopy(codeBuffer, 0, bigger, 0, codeBuffer.length);
codeBuffer = 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);
// }
}