Code generation / compilation complete.
parent
65e5c05c70
commit
ab84203b61
|
@ -1,71 +0,0 @@
|
|||
package tester;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/* Automated regression tester for Checkpoint 1 tests
|
||||
* Created by Max Beckman-Harned
|
||||
* Put your tests in "tests/pa1_tests" folder in your Eclipse workspace directory
|
||||
*/
|
||||
public class Checkpoint1 {
|
||||
|
||||
static ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
File testDir = new File(System.getProperty("java.class.path") + "/../tests/pa1_tests");
|
||||
System.out.println(System.getProperty("java.class.path"));
|
||||
int failures = 0;
|
||||
for (File x : testDir.listFiles()) {
|
||||
int returnCode = runTest(x);
|
||||
if (x.getName().indexOf("pass") != -1) {
|
||||
if (returnCode == 0)
|
||||
System.out.println(x.getName() + " passed successfully!");
|
||||
else {
|
||||
failures++;
|
||||
System.err.println(x.getName()
|
||||
+ " failed but should have passed!");
|
||||
}
|
||||
} else {
|
||||
if (returnCode == 4)
|
||||
System.out.println(x.getName() + " failed successfully!");
|
||||
else {
|
||||
System.err.println(x.getName() + " did not fail properly!");
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println(failures + " failures in all.");
|
||||
}
|
||||
|
||||
private static int runTest(File x) throws IOException, InterruptedException {
|
||||
ProcessBuilder pb = new ProcessBuilder("java", "miniJava.Compiler", x.getPath()).directory(new File(System.getProperty("java.class.path")));
|
||||
Process p = pb.start();
|
||||
threadPool.execute(new ProcessOutputter(p.getInputStream(), false));
|
||||
p.waitFor();
|
||||
return p.exitValue();
|
||||
}
|
||||
|
||||
static class ProcessOutputter implements Runnable {
|
||||
private Scanner processOutput;
|
||||
private boolean output;
|
||||
|
||||
public ProcessOutputter(InputStream _processStream, boolean _output) {
|
||||
processOutput = new Scanner(_processStream);
|
||||
output = _output;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
while(processOutput.hasNextLine()) {
|
||||
String line = processOutput.nextLine();
|
||||
if (output)
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package mJAM;
|
||||
|
||||
public class Assembler {
|
||||
// TBD
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
/**
|
||||
* mJAM instruction format
|
||||
* @author prins
|
||||
* @version COMP 520 V2.2
|
||||
*/
|
||||
package mJAM;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Disassemble the mJAM object code
|
||||
* from input file xxx.mJAM
|
||||
* into output file xxx.asm
|
||||
*
|
||||
* @author prins
|
||||
* @version COMP 520 v2.2
|
||||
*/
|
||||
public class Disassembler {
|
||||
|
||||
private String objectFileName;
|
||||
private String asmName;
|
||||
private FileWriter asmOut;
|
||||
private boolean error = false;
|
||||
private Map<Integer, String> addrToLabel;
|
||||
|
||||
public Disassembler(String objectFileName) {
|
||||
this.objectFileName = objectFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the r-field of an instruction in the form "l<I>reg</I>r", where
|
||||
* l and r are the bracket characters to use.
|
||||
* @param leftbracket the character to print before the register.
|
||||
* @param r the number of the register.
|
||||
* @param rightbracket the character to print after the register.
|
||||
*/
|
||||
private void writeR(char leftbracket, int r, char rightbracket) {
|
||||
asmWrite(Character.toString(leftbracket));
|
||||
asmWrite(Machine.intToReg[r].toString());
|
||||
asmWrite(Character.toString(rightbracket));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a void n-field of an instruction.
|
||||
*/
|
||||
private void blankN() {
|
||||
asmWrite(" ");
|
||||
}
|
||||
|
||||
// Writes the n-field of an instruction.
|
||||
/**
|
||||
* Writes the n-field of an instruction in the form "(n)".
|
||||
* @param n the integer to write.
|
||||
*/
|
||||
private void writeN(int n) {
|
||||
asmWrite(String.format("%-6s","(" + n + ")"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the d-field of an instruction.
|
||||
* @param d the integer to write.
|
||||
*/
|
||||
private void writeD(int d) {
|
||||
asmWrite(Integer.toString(d));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the name of primitive routine with relative address d.
|
||||
* @param d the displacment of the primitive routine.
|
||||
*/
|
||||
private void writePrimitive(int d) {
|
||||
Machine.Prim prim = Machine.intToPrim[d];
|
||||
asmWrite(String.format("%-8s",prim.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given instruction in assembly-code format.
|
||||
* @param instr the instruction to display.
|
||||
*/
|
||||
private void writeInstruction(Instruction instr) {
|
||||
|
||||
String targetLabel = "***";
|
||||
// get label of destination addr, if instr transfers control
|
||||
if (instr.r == Machine.Reg.CB.ordinal())
|
||||
targetLabel = addrToLabel.get(instr.d);
|
||||
|
||||
Machine.Op instruction = Machine.intToOp[instr.op];
|
||||
asmWrite(String.format("%-7s",instruction.toString()));
|
||||
switch (instruction) {
|
||||
case LOAD:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
writeR('[', instr.r, ']');
|
||||
break;
|
||||
|
||||
case LOADA:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
writeR('[', instr.r, ']');
|
||||
break;
|
||||
|
||||
case LOADI:
|
||||
break;
|
||||
|
||||
case LOADL:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
break;
|
||||
|
||||
case STORE:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
writeR('[', instr.r, ']');
|
||||
break;
|
||||
|
||||
case STOREI:
|
||||
break;
|
||||
|
||||
case CALL:
|
||||
if (instr.r == Machine.Reg.PB.ordinal()) {
|
||||
blankN();
|
||||
writePrimitive(instr.d);
|
||||
} else {
|
||||
blankN();
|
||||
asmWrite(targetLabel);
|
||||
}
|
||||
break;
|
||||
|
||||
case CALLI:
|
||||
blankN();
|
||||
asmWrite(targetLabel);
|
||||
break;
|
||||
|
||||
case RETURN:
|
||||
writeN(instr.n);
|
||||
writeD(instr.d);
|
||||
break;
|
||||
|
||||
case CALLD:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
break;
|
||||
|
||||
case PUSH:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
break;
|
||||
|
||||
case POP:
|
||||
blankN();
|
||||
writeD(instr.d);
|
||||
break;
|
||||
|
||||
case JUMP:
|
||||
blankN();
|
||||
asmWrite(targetLabel);
|
||||
break;
|
||||
|
||||
case JUMPI:
|
||||
break;
|
||||
|
||||
case JUMPIF:
|
||||
writeN(instr.n);
|
||||
asmWrite(targetLabel);
|
||||
break;
|
||||
|
||||
case HALT:
|
||||
writeN(instr.n);
|
||||
break;
|
||||
|
||||
default:
|
||||
asmWrite("???? ");
|
||||
writeN(instr.n);
|
||||
writeD(instr.d);
|
||||
writeR('[', instr.r, ']');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disassembles program held in code store
|
||||
*/
|
||||
void disassembleProgram(String asmFileName) {
|
||||
|
||||
try {
|
||||
asmOut = new FileWriter(asmFileName);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Disassembler: can not create asm output file "
|
||||
+ asmName);
|
||||
error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// collect all addresses that may be the target of a jump instruction
|
||||
SortedSet<Integer> targets = new TreeSet<Integer>();
|
||||
for (int addr = Machine.CB; addr < Machine.CT; addr++) {
|
||||
Instruction inst = Machine.code[addr];
|
||||
Machine.Op op = Machine.intToOp[inst.op];
|
||||
switch (op) {
|
||||
case CALL:
|
||||
case CALLI:
|
||||
// only consider calls (branches) within code memory (i.e. not primitives)
|
||||
if (inst.r == Machine.Reg.CB.ordinal())
|
||||
targets.add(inst.d);
|
||||
break;
|
||||
case JUMP:
|
||||
// address following an unconditional branch is an implicit target
|
||||
targets.add(addr+1);
|
||||
targets.add(inst.d);
|
||||
break;
|
||||
case JUMPIF:
|
||||
// a jump of any sort creates a branch target
|
||||
targets.add(inst.d);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// map branch target addresses to unique labels
|
||||
addrToLabel = new HashMap<Integer, String>();
|
||||
int labelCounter = 10;
|
||||
for (Integer addr : targets) {
|
||||
String label = "L" + labelCounter++ ;
|
||||
addrToLabel.put(addr, label);
|
||||
}
|
||||
|
||||
// disassemble each instruction
|
||||
for (int addr = Machine.CB; addr < Machine.CT; addr++) {
|
||||
|
||||
// generate instruction address
|
||||
asmWrite(String.format("%3d ", addr));
|
||||
|
||||
// if this addr is a branch target, output label
|
||||
if (addrToLabel.containsKey(addr))
|
||||
asmWrite(String.format("%-7s", addrToLabel.get(addr) + ":"));
|
||||
else
|
||||
asmWrite(" ");
|
||||
|
||||
// instruction
|
||||
writeInstruction(Machine.code[addr]);
|
||||
|
||||
// newline
|
||||
asmWrite("\n");
|
||||
}
|
||||
|
||||
// close output file
|
||||
try {
|
||||
asmOut.close();
|
||||
} catch (IOException e) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void asmWrite(String s) {
|
||||
try {
|
||||
asmOut.write(s);
|
||||
} catch (IOException e) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("********** mJAM Disassembler (1.0) **********");
|
||||
String objectFileName = "obj.mJAM";
|
||||
if (args.length == 1)
|
||||
objectFileName = args[0];
|
||||
Disassembler d = new Disassembler(objectFileName);
|
||||
d.disassemble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassemble object file
|
||||
* @return true if error encountered else false
|
||||
*/
|
||||
public boolean disassemble() {
|
||||
ObjectFile objectFile = new ObjectFile(objectFileName);
|
||||
|
||||
// read object file into code store
|
||||
if (objectFile.read()) {
|
||||
System.out.println("Disassembler: unable to read object file"
|
||||
+ objectFileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
// assembler-code output file name
|
||||
if (objectFileName.endsWith(".mJAM"))
|
||||
asmName = objectFileName.substring(0, objectFileName.length() - 5)
|
||||
+ ".asm";
|
||||
else
|
||||
asmName = objectFileName + ".asm";
|
||||
|
||||
// disassemble to file
|
||||
disassembleProgram(asmName);
|
||||
|
||||
if (error) {
|
||||
System.out.println("Disassembler: unable to write asm file"
|
||||
+ asmName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* mJAM instruction format
|
||||
* @author prins
|
||||
* @version COMP 520 V2.2
|
||||
*/
|
||||
package mJAM;
|
||||
|
||||
public class Instruction {
|
||||
|
||||
public Instruction() {
|
||||
op = 0;
|
||||
r = 0;
|
||||
n = 0;
|
||||
d = 0;
|
||||
}
|
||||
|
||||
public Instruction(int op, int n, int r, int d) {
|
||||
this.op = op;
|
||||
this.n = n;
|
||||
this.r = r;
|
||||
this.d = d;
|
||||
}
|
||||
|
||||
// Java has no type synonyms, so the following representations are
|
||||
// assumed:
|
||||
//
|
||||
// type
|
||||
// OpCode = 0..15; {4 bits unsigned}
|
||||
// Register = 0..15; (4 bits unsigned)
|
||||
// Length = 0..255; {8 bits unsigned}
|
||||
// Operand = -2147483648 .. +2147483647; (32 bits signed for use with LOADL)
|
||||
public int op; // OpCode
|
||||
public int r; // RegisterNumber
|
||||
public int n; // Length
|
||||
public int d; // Operand
|
||||
}
|
|
@ -0,0 +1,888 @@
|
|||
/**
|
||||
* Interprets mJAM programs
|
||||
* @author prins
|
||||
* @version COMP 520 V2.2
|
||||
*/
|
||||
package mJAM;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Scanner;
|
||||
|
||||
// import mJAM.Machine.Reg;
|
||||
|
||||
public class Interpreter {
|
||||
|
||||
// DATA STORE
|
||||
static int[] data = new int[1024];
|
||||
|
||||
// DATA STORE REGISTERS AND OTHER REGISTERS
|
||||
final static int CB = 0, SB = 0, HB = 1024; // = upper bound of data array + 1
|
||||
|
||||
|
||||
static int CT, CP, ST, HT, LB, OB, status, temp;
|
||||
|
||||
// machine status values
|
||||
final static int running = 0, halted = 1, failedDataStoreFull = 2,
|
||||
failedInvalidCodeAddress = 3, failedInvalidInstruction = 4,
|
||||
failedOverflow = 5, failedZeroDivide = 6, failedIOError = 7,
|
||||
failedArrayIndex = 8, failedNullRef = 9, failedHeapRef =10,
|
||||
failedMethodIndex = 11;
|
||||
|
||||
static long accumulator;
|
||||
|
||||
// Debugger state
|
||||
enum DebuggerStatus {
|
||||
PAUSED, RUNNING
|
||||
}
|
||||
|
||||
static DebuggerStatus debuggerStatus = DebuggerStatus.PAUSED;
|
||||
static ArrayList<Integer> breakpoints = new ArrayList<Integer>();
|
||||
static ArrayList<String> sourceLines;
|
||||
|
||||
static int content(int r) {
|
||||
// Returns the current content of register r,
|
||||
Machine.Reg reg = Machine.intToReg[r];
|
||||
switch (reg) {
|
||||
case CB:
|
||||
return CB;
|
||||
case CT:
|
||||
return CT;
|
||||
case PB:
|
||||
return Machine.PB;
|
||||
case PT:
|
||||
return Machine.PT;
|
||||
case SB:
|
||||
return SB;
|
||||
case ST:
|
||||
return ST;
|
||||
case HB:
|
||||
return HB;
|
||||
case HT:
|
||||
return HT;
|
||||
case LB:
|
||||
return LB;
|
||||
case OB:
|
||||
return OB;
|
||||
case CP:
|
||||
return CP;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// PROGRAM STATUS
|
||||
|
||||
static void dump() {
|
||||
// Writes a summary of the machine state.
|
||||
int addr, dynamicLink;
|
||||
System.out.println("");
|
||||
System.out.println("At instruction " + CP
|
||||
+ ", state of mJAM data store and registers is:");
|
||||
System.out.println("");
|
||||
if (HT == HB)
|
||||
System.out.println(" |--------| (heap is empty)");
|
||||
else {
|
||||
System.out.println(" HB--> ");
|
||||
System.out.println(" |--------|");
|
||||
for (addr = HB - 1; addr >= HT; addr--) {
|
||||
System.out.print(rightPad(6, addr + ":"));
|
||||
if (addr == OB)
|
||||
System.out.print("OB--> ");
|
||||
else if (addr == HT)
|
||||
System.out.print("HT--> ");
|
||||
else
|
||||
System.out.print(" ");
|
||||
System.out.println("|" + leftPad(8, String.valueOf(data[addr]))
|
||||
+ "|");
|
||||
}
|
||||
System.out.println(" |--------|");
|
||||
}
|
||||
System.out.println(" |////////|");
|
||||
System.out.println(" |////////|");
|
||||
if (ST == SB)
|
||||
System.out.println(" |--------| (stack is empty)");
|
||||
else {
|
||||
dynamicLink = LB;
|
||||
System.out.println(" ST--> |////////|");
|
||||
System.out.println(" |--------|");
|
||||
for (addr = ST - 1; addr >= SB; addr--) {
|
||||
System.out.print(rightPad(6, addr + ": "));
|
||||
if (addr == SB)
|
||||
System.out.print("SB--> ");
|
||||
else if (addr == LB)
|
||||
System.out.print("LB--> ");
|
||||
else
|
||||
System.out.print(" ");
|
||||
if ((addr == dynamicLink) && (dynamicLink != SB))
|
||||
System.out.print("|OB="
|
||||
+ leftPad(5, String.valueOf(data[addr])) + "|");
|
||||
else if ((addr == dynamicLink + 1) && (dynamicLink != SB))
|
||||
System.out.print("|DL="
|
||||
+ leftPad(5, String.valueOf(data[addr])) + "|");
|
||||
else if ((addr == dynamicLink + 2) && (dynamicLink != SB))
|
||||
System.out.print("|RA="
|
||||
+ leftPad(5, String.valueOf(data[addr])) + "|");
|
||||
else
|
||||
System.out.print("|"
|
||||
+ leftPad(8, String.valueOf(data[addr])) + "|");
|
||||
System.out.println("");
|
||||
if (addr == dynamicLink) {
|
||||
System.out.println(" |--------|");
|
||||
dynamicLink = data[addr + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("");
|
||||
}
|
||||
|
||||
private static String leftPad(int len, String s) {
|
||||
int aLen = Math.max(len, s.length());
|
||||
StringBuffer buf = new StringBuffer(s);
|
||||
String r = buf.insert(0, " ").toString();
|
||||
return r.substring(r.length() - aLen, r.length());
|
||||
}
|
||||
|
||||
private static String rightPad(int len, String s) {
|
||||
int aLen = Math.max(len, s.length());
|
||||
String r = s + " ";
|
||||
return r.substring(0, aLen);
|
||||
}
|
||||
|
||||
static void showStatus() {
|
||||
// Writes an indication of whether and why the program has terminated.
|
||||
System.out.println("");
|
||||
System.out.print("*** ");
|
||||
switch (status) {
|
||||
case running:
|
||||
System.out.println("Program is running.");
|
||||
break;
|
||||
case halted:
|
||||
System.out.println("Program has halted normally.");
|
||||
break;
|
||||
case failedDataStoreFull:
|
||||
System.out.println("Program has failed due to exhaustion of Data Store.");
|
||||
break;
|
||||
case failedInvalidCodeAddress:
|
||||
System.out.println("Program has failed due to an invalid code address.");
|
||||
break;
|
||||
case failedInvalidInstruction:
|
||||
System.out.println("Program has failed due to an invalid instruction.");
|
||||
break;
|
||||
case failedOverflow:
|
||||
System.out.println("Program has failed due to overflow.");
|
||||
break;
|
||||
case failedZeroDivide:
|
||||
System.out.println("Program has failed due to division by zero.");
|
||||
break;
|
||||
case failedIOError:
|
||||
System.out.println("Program has failed due to an IO error.");
|
||||
break;
|
||||
case failedArrayIndex:
|
||||
System.out.println("Program has failed due to an array index error.");
|
||||
break;
|
||||
case failedNullRef:
|
||||
System.out.println("Program has failed due to a null pointer reference.");
|
||||
break;
|
||||
case failedHeapRef:
|
||||
System.out.println("Program has failed due to an invalid Heap reference.");
|
||||
break;
|
||||
case failedMethodIndex:
|
||||
System.out.println("Program has failed due to an improper method index in CALLD.");
|
||||
break;
|
||||
default:
|
||||
System.out.println("Machine is in an unknown state.");
|
||||
break;
|
||||
}
|
||||
if (status != halted)
|
||||
dump();
|
||||
}
|
||||
|
||||
// INTERPRETATION
|
||||
|
||||
static void checkSpace(int spaceNeeded) {
|
||||
// Signals failure if there is not enough space to expand the stack or
|
||||
// heap by spaceNeeded.
|
||||
if (HT - ST < spaceNeeded)
|
||||
status = failedDataStoreFull;
|
||||
}
|
||||
|
||||
static boolean invalidHeapRef(int addr) {
|
||||
// if addr is null ptr or outside of heap bounds, sets status to failure
|
||||
if (addr == Machine.nullRep)
|
||||
status = failedNullRef;
|
||||
else if (addr < HT + 2 || addr >= HB)
|
||||
status = failedHeapRef;
|
||||
return (status != running);
|
||||
}
|
||||
|
||||
static boolean isTrue(int datum) {
|
||||
// Tests whether the given datum represents true.
|
||||
return (datum == Machine.trueRep);
|
||||
}
|
||||
|
||||
static int overflowChecked(long datum) {
|
||||
// Signals failure if the datum is too large to fit into a single word,
|
||||
// otherwise returns the datum as a single word.
|
||||
if ((Machine.minintRep <= datum) && (datum <= Machine.maxintRep))
|
||||
return (int) datum;
|
||||
else {
|
||||
status = failedOverflow;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int toInt(boolean b) {
|
||||
return b ? Machine.trueRep : Machine.falseRep;
|
||||
}
|
||||
|
||||
static int currentChar;
|
||||
|
||||
static int readInt() throws java.io.IOException {
|
||||
int temp = 0;
|
||||
int sign = 1;
|
||||
|
||||
do {
|
||||
currentChar = System.in.read();
|
||||
} while (Character.isWhitespace((char) currentChar));
|
||||
|
||||
if ((currentChar == '-') || (currentChar == '+'))
|
||||
do {
|
||||
sign = (currentChar == '-') ? -1 : 1;
|
||||
currentChar = System.in.read();
|
||||
} while ((currentChar == '-') || currentChar == '+');
|
||||
|
||||
if (Character.isDigit((char) currentChar))
|
||||
do {
|
||||
temp = temp * 10 + (currentChar - '0');
|
||||
currentChar = System.in.read();
|
||||
} while (Character.isDigit((char) currentChar));
|
||||
|
||||
return sign * temp;
|
||||
}
|
||||
|
||||
// Invoke primitive operation with argument(s) on the stack
|
||||
// primitives are static and are not supplied an instance on the stack.
|
||||
static void callPrimitive(int id) {
|
||||
|
||||
int addr, size, index;
|
||||
char ch;
|
||||
|
||||
Machine.Prim prim = Machine.intToPrim[id];
|
||||
switch (prim) {
|
||||
case id:
|
||||
break; // nothing to be done
|
||||
case not:
|
||||
data[ST - 1] = toInt(!isTrue(data[ST - 1]));
|
||||
break;
|
||||
case and:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(isTrue(data[ST - 1]) & isTrue(data[ST]));
|
||||
break;
|
||||
case or:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(isTrue(data[ST - 1]) | isTrue(data[ST]));
|
||||
break;
|
||||
case succ:
|
||||
data[ST - 1] = overflowChecked(data[ST - 1] + 1);
|
||||
break;
|
||||
case pred:
|
||||
data[ST - 1] = overflowChecked(data[ST - 1] - 1);
|
||||
break;
|
||||
case neg:
|
||||
data[ST - 1] = overflowChecked(-data[ST - 1]);
|
||||
break;
|
||||
case add:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST - 1];
|
||||
data[ST - 1] = overflowChecked(accumulator + data[ST]);
|
||||
break;
|
||||
case sub:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST - 1];
|
||||
data[ST - 1] = overflowChecked(accumulator - data[ST]);
|
||||
break;
|
||||
case mult:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST - 1];
|
||||
data[ST - 1] = overflowChecked(accumulator * data[ST]);
|
||||
break;
|
||||
case div:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST - 1];
|
||||
if (data[ST] != 0)
|
||||
data[ST - 1] = (int) (accumulator / data[ST]);
|
||||
else
|
||||
status = failedZeroDivide;
|
||||
break;
|
||||
case mod:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST - 1];
|
||||
if (data[ST] != 0)
|
||||
data[ST - 1] = (int) (accumulator % data[ST]);
|
||||
else
|
||||
status = failedZeroDivide;
|
||||
break;
|
||||
case lt:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(data[ST - 1] < data[ST]);
|
||||
break;
|
||||
case le:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(data[ST - 1] <= data[ST]);
|
||||
break;
|
||||
case ge:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(data[ST - 1] >= data[ST]);
|
||||
break;
|
||||
case gt:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(data[ST - 1] > data[ST]);
|
||||
break;
|
||||
case eq:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(data[ST - 1] == data[ST]);
|
||||
break;
|
||||
case ne:
|
||||
ST = ST - 1;
|
||||
data[ST - 1] = toInt(data[ST - 1] != data[ST]);
|
||||
break;
|
||||
case eol:
|
||||
data[ST] = toInt(currentChar == '\n');
|
||||
ST = ST + 1;
|
||||
break;
|
||||
case eof:
|
||||
data[ST] = toInt(currentChar == -1);
|
||||
ST = ST + 1;
|
||||
break;
|
||||
case get:
|
||||
ST = ST - 1;
|
||||
addr = data[ST];
|
||||
try {
|
||||
currentChar = System.in.read();
|
||||
} catch (java.io.IOException s) {
|
||||
status = failedIOError;
|
||||
}
|
||||
data[addr] = (int) currentChar;
|
||||
break;
|
||||
case put:
|
||||
ST = ST - 1;
|
||||
ch = (char) data[ST];
|
||||
System.out.print(ch);
|
||||
break;
|
||||
case geteol:
|
||||
try {
|
||||
while ((currentChar = System.in.read()) != '\n')
|
||||
;
|
||||
} catch (java.io.IOException s) {
|
||||
status = failedIOError;
|
||||
}
|
||||
break;
|
||||
case puteol:
|
||||
System.out.println("");
|
||||
break;
|
||||
case getint:
|
||||
ST = ST - 1;
|
||||
addr = data[ST];
|
||||
try {
|
||||
accumulator = readInt();
|
||||
} catch (java.io.IOException s) {
|
||||
status = failedIOError;
|
||||
}
|
||||
data[addr] = (int) accumulator;
|
||||
break;
|
||||
case putint:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST];
|
||||
System.out.print(accumulator);
|
||||
break;
|
||||
// output with prefix for tester
|
||||
case putintnl:
|
||||
ST = ST - 1;
|
||||
accumulator = data[ST];
|
||||
System.out.print(">>> " + accumulator + "\n");
|
||||
break;
|
||||
case alloc:
|
||||
size = data[ST - 1];
|
||||
checkSpace(size);
|
||||
HT = HT - size;
|
||||
data[ST - 1] = HT;
|
||||
break;
|
||||
case dispose:
|
||||
ST = ST - 1; // no action taken at present
|
||||
break;
|
||||
case newobj:
|
||||
// ..., class obj addr, number of fields ==> ..., new obj addr
|
||||
size = data[ST - 1] + 2; // number of fields + 2 word descriptor
|
||||
checkSpace(size);
|
||||
HT = HT - size; // reserve space
|
||||
data[HT] = data[ST - 2]; // set class object addr
|
||||
data[HT + 1] = size - 2; // set size of object
|
||||
data[ST - 2] = HT + 2; // addr of new object instance, returned on stack
|
||||
ST = ST - 1; // net effect of pop 2 args, push 1 result
|
||||
for (int i = 2; i < size; i++) {
|
||||
data[HT + i] = 0; // zero all fields of new object
|
||||
}
|
||||
break;
|
||||
case newarr:
|
||||
// ..., number of elements ==> ..., new int[] addr
|
||||
size = data[ST - 1] + 2; // array + 2 word descriptor
|
||||
checkSpace(size);
|
||||
HT = HT - size;
|
||||
data[HT] = -2; // tag for array
|
||||
data[HT + 1] = size - 2; // size of array
|
||||
data[ST - 1] = HT + 2; // addr of array instance, returned on stack
|
||||
for (int i = 2; i < size; i++) {
|
||||
data[HT + i] = 0; // zero all elements of new array
|
||||
}
|
||||
break;
|
||||
case arrayref:
|
||||
// ..., array addr a, element index i ==> ..., a[i]
|
||||
addr = data[ST - 2];
|
||||
if (invalidHeapRef(addr))
|
||||
break;
|
||||
index = data[ST - 1];
|
||||
if (data[addr - 2] != -2 || index < 0 || index >= data[addr - 1]) {
|
||||
status = failedArrayIndex;
|
||||
break;
|
||||
}
|
||||
data[ST - 2] = data[addr + index]; // result element, returned on stack
|
||||
ST = ST - 1; // pop two args, return one result
|
||||
break;
|
||||
case arrayupd:
|
||||
// ..., array addr a, element index i, new value v ==> ...
|
||||
// and a[i] := v
|
||||
addr = data[ST - 3];
|
||||
if (invalidHeapRef(addr))
|
||||
break;
|
||||
index = data[ST - 2];
|
||||
if (data[addr - 2] != -2 || index < 0 || index >= data[addr - 1]) {
|
||||
status = failedArrayIndex;
|
||||
break;
|
||||
}
|
||||
data[addr + index] = data[ST - 1]; // update array element
|
||||
ST = ST - 3; // pop 3 args, return no result
|
||||
break;
|
||||
case fieldref:
|
||||
// ..., obj addr a, field index i ==> ..., value of ith field of a
|
||||
addr = data[ST - 2];
|
||||
if (invalidHeapRef(addr))
|
||||
break;
|
||||
index = data[ST - 1];
|
||||
if (index < 0 || index >= data[addr - 1]) {
|
||||
status = failedArrayIndex;
|
||||
break;
|
||||
}
|
||||
data[ST - 2] = data[addr + index]; // field to stack top
|
||||
ST = ST - 1; // pop two args, return one result
|
||||
break;
|
||||
case fieldupd:
|
||||
// ..., obj addr a, field index i, new value v ==> ...
|
||||
// and a.i := v
|
||||
addr = data[ST - 3];
|
||||
if (invalidHeapRef(addr))
|
||||
break;
|
||||
index = data[ST - 2];
|
||||
if (index < 0 || index >= data[addr - 1]) {
|
||||
status = failedArrayIndex;
|
||||
break;
|
||||
}
|
||||
data[addr + index] = data[ST - 1]; // update field to new value
|
||||
ST = ST - 3; // pop 3 args, return no result
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void interpretOneOperation() {
|
||||
// Fetch instruction ...
|
||||
Instruction currentInstr = Machine.code[CP];
|
||||
// Decode instruction ...
|
||||
int op = currentInstr.op;
|
||||
int r = currentInstr.r;
|
||||
int n = currentInstr.n;
|
||||
int d = currentInstr.d;
|
||||
int addr;
|
||||
// Execute instruction ...
|
||||
|
||||
Machine.Op operation = Machine.intToOp[op];
|
||||
|
||||
switch (operation) {
|
||||
case LOAD:
|
||||
addr = d + content(r);
|
||||
checkSpace(1);
|
||||
data[ST] = data[addr];
|
||||
ST = ST + 1;
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case LOADA:
|
||||
addr = d + content(r);
|
||||
checkSpace(1);
|
||||
data[ST] = addr;
|
||||
ST = ST + 1;
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case LOADI:
|
||||
ST = ST - 1;
|
||||
addr = data[ST];
|
||||
checkSpace(1);
|
||||
data[ST] = data[addr];
|
||||
ST = ST + 1;
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case LOADL:
|
||||
checkSpace(1);
|
||||
data[ST] = d;
|
||||
ST = ST + 1;
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case STORE:
|
||||
addr = d + content(r);
|
||||
ST = ST - 1;
|
||||
data[addr] = data[ST];
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case STOREI:
|
||||
ST = ST - 1;
|
||||
addr = data[ST];
|
||||
ST = ST - 1;
|
||||
data[addr] = data[ST];
|
||||
CP = CP + 1;
|
||||
break;
|
||||
|
||||
case CALL:
|
||||
// call static method, including primitives
|
||||
// arguments are on stack
|
||||
addr = d + content(r); // effective address
|
||||
if (addr >= Machine.PB) {
|
||||
callPrimitive(addr - Machine.PB);
|
||||
CP = CP + 1;
|
||||
} else {
|
||||
// static method in code segment, no instance addr on stack
|
||||
checkSpace(3);
|
||||
data[ST] = OB; // save caller OB in callee frame
|
||||
data[ST + 1] = LB; // save caller LB in callee frame (dynamic link)
|
||||
data[ST + 2] = CP + 1; // save caller return address in callee frame
|
||||
OB = Machine.nullRep; // set callee OB (null since no instance)
|
||||
LB = ST; // set LB = start of callee frame
|
||||
ST = ST + 3; // set ST = end of callee frame
|
||||
CP = addr; // execution resumes at addr specified in CALL inst
|
||||
}
|
||||
break;
|
||||
|
||||
case CALLI:
|
||||
// call instance method
|
||||
// arguments on stack, followed by instance address
|
||||
addr = d + content(r); // effective address
|
||||
if (addr >= Machine.CT) {
|
||||
// no instance methods outside of code segment
|
||||
status = failedInvalidInstruction;
|
||||
break;
|
||||
}
|
||||
// instance address is last arg on stack and is overwritten by frame
|
||||
checkSpace(2);
|
||||
temp = data[ST - 1]; // save instance address temporarily
|
||||
data[ST - 1] = OB; // save caller OB in callee frame
|
||||
data[ST] = LB; // save caller LB in callee frame (dynamic link)
|
||||
data[ST + 1] = CP + 1; // save caller return address in callee frame
|
||||
OB = temp; // set OB for callee
|
||||
LB = ST - 1; // set LB = start of callee frame
|
||||
ST = ST + 2; // set ST = end of callee frame
|
||||
CP = addr; // execution resumes at addr specified in CALL inst
|
||||
break;
|
||||
|
||||
case RETURN:
|
||||
// d = number of method args (does not include instance addr for CALLI)
|
||||
// n = size of result (0 or 1)
|
||||
if (n < 0 || n > 1) {
|
||||
status = failedInvalidInstruction;
|
||||
break;
|
||||
}
|
||||
addr = LB - d; // addr of caller args
|
||||
OB = data[LB]; // restore caller OB, LB, CP
|
||||
CP = data[LB + 2];
|
||||
LB = data[LB + 1];
|
||||
if (n == 1)
|
||||
data[addr] = data[ST - 1]; // return value if any
|
||||
ST = addr + n; // caller stack top
|
||||
break;
|
||||
|
||||
case CALLD:
|
||||
// dynamic method dispatch of method with index d (origin 0)
|
||||
// arguments on stack, followed by instance addr
|
||||
{
|
||||
addr = data[ST - 1]; // instance addr
|
||||
if (invalidHeapRef(addr))
|
||||
break;
|
||||
int classDescAddr = data[addr - 2];
|
||||
if (classDescAddr >= ST || classDescAddr <= SB || d >= data[classDescAddr + 1] || d < 0) {
|
||||
status = failedMethodIndex;
|
||||
break;
|
||||
}
|
||||
ST = ST - 1;
|
||||
checkSpace(3);
|
||||
data[ST] = OB;
|
||||
data[ST + 1] = LB;
|
||||
data[ST + 2] = CP + 1;
|
||||
OB = addr;
|
||||
LB = ST;
|
||||
ST = ST + 3;
|
||||
CP = data[classDescAddr + 2 + n];
|
||||
}
|
||||
break;
|
||||
case PUSH: // push d elements on stack
|
||||
checkSpace(d);
|
||||
ST = ST + d;
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case POP: // pop d elements off stack
|
||||
ST = ST - d;
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case JUMP:
|
||||
CP = d + content(r);
|
||||
break;
|
||||
case JUMPI:
|
||||
ST = ST - 1;
|
||||
CP = data[ST];
|
||||
break;
|
||||
case JUMPIF:
|
||||
ST = ST - 1;
|
||||
if (data[ST] == n)
|
||||
CP = d + content(r);
|
||||
else
|
||||
CP = CP + 1;
|
||||
break;
|
||||
case HALT:
|
||||
if (n > 0) {
|
||||
// halt n > 0 --> snapshot machine state and continue execution
|
||||
dump();
|
||||
CP = CP + 1;
|
||||
} else
|
||||
status = halted;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((CP < CB) || (CP >= CT))
|
||||
status = failedInvalidCodeAddress;
|
||||
|
||||
if (breakpoints.indexOf(CP) != -1) {
|
||||
debuggerStatus = DebuggerStatus.PAUSED;
|
||||
System.out.println("Breakpoint hit: " + sourceLines.get(CP));
|
||||
}
|
||||
}
|
||||
|
||||
static void initMachine() {
|
||||
// Initialize registers ...
|
||||
ST = SB;
|
||||
HT = HB;
|
||||
LB = SB;
|
||||
CP = CB;
|
||||
OB = -1; // invalid instance addr
|
||||
CT = Machine.CT;
|
||||
status = running;
|
||||
}
|
||||
|
||||
static void interpretProgram() {
|
||||
// Runs the program in code store.
|
||||
initMachine();
|
||||
do {
|
||||
interpretOneOperation();
|
||||
} while (status == running);
|
||||
}
|
||||
|
||||
static void runProgramFromStart() {
|
||||
initMachine();
|
||||
continueProgram();
|
||||
}
|
||||
|
||||
static void continueProgram() {
|
||||
debuggerStatus = DebuggerStatus.RUNNING;
|
||||
do {
|
||||
interpretOneOperation();
|
||||
} while (status == running && debuggerStatus == DebuggerStatus.RUNNING);
|
||||
}
|
||||
|
||||
static void printHelp() {
|
||||
String[] help = {
|
||||
"p or print:",
|
||||
" print entire machine state",
|
||||
"l or list [offset] [size]:",
|
||||
" print the instructions around CP + offset, with size lines on either side",
|
||||
" offset = 0 and size = 2 by default",
|
||||
"b or break [address]:",
|
||||
" set a breakpoint at address",
|
||||
" address = CP by default",
|
||||
"del:",
|
||||
" delete one or more breakpoints",
|
||||
"n or next:",
|
||||
" execute one instruction",
|
||||
"c or continue:",
|
||||
" continue running the program from current position, until next breakpoint or completion",
|
||||
"r or run:",
|
||||
" run the program from start, until next breakpoint or completion",
|
||||
"i or info:", " list the current breakpoints",
|
||||
"q, quit or <EOF>:", " quit the debugger",
|
||||
"Simply press enter to repeat the last command", "? or help:",
|
||||
" print this help" };
|
||||
|
||||
for (String line : help) {
|
||||
System.out.println(" " + line);
|
||||
}
|
||||
}
|
||||
|
||||
static void debugProgram() {
|
||||
initMachine();
|
||||
|
||||
BufferedReader inputReader = new BufferedReader(new InputStreamReader(
|
||||
System.in));
|
||||
|
||||
String lastCommand = "";
|
||||
|
||||
while (true) {
|
||||
System.out.print("\n: ");
|
||||
String inputLine;
|
||||
|
||||
try {
|
||||
inputLine = inputReader.readLine();
|
||||
} catch (IOException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputLine == null)
|
||||
return;
|
||||
|
||||
Scanner scanner = new Scanner(inputLine);
|
||||
String command = (scanner.hasNext() ? scanner.next() : lastCommand);
|
||||
lastCommand = command;
|
||||
|
||||
if (command.equals("?") || command.equals("help")) {
|
||||
printHelp();
|
||||
} else if (command.equalsIgnoreCase("p")
|
||||
|| command.equalsIgnoreCase("print")) {
|
||||
dump();
|
||||
} else if (command.equalsIgnoreCase("l")
|
||||
|| command.equalsIgnoreCase("list")) {
|
||||
int offset = 0, size = 2;
|
||||
if (scanner.hasNextInt())
|
||||
offset = scanner.nextInt();
|
||||
if (scanner.hasNextInt())
|
||||
size = scanner.nextInt();
|
||||
|
||||
for (int i = CP + offset - size; i <= CP + offset + size; ++i) {
|
||||
if (i >= 0 && i < sourceLines.size())
|
||||
System.out.println((i == CP ? " >" : " ")
|
||||
+ sourceLines.get(i));
|
||||
}
|
||||
} else if (command.equalsIgnoreCase("b")
|
||||
|| command.equalsIgnoreCase("break")) {
|
||||
int addr = scanner.hasNextInt() ? scanner.nextInt() : CP;
|
||||
if (!breakpoints.contains(addr))
|
||||
breakpoints.add(addr);
|
||||
System.out.println("Added breakpoint at "
|
||||
+ sourceLines.get(addr));
|
||||
} else if (command.equalsIgnoreCase("del")) {
|
||||
while (scanner.hasNextInt()) {
|
||||
int addr = scanner.nextInt(), idx = breakpoints
|
||||
.indexOf(addr);
|
||||
if (idx != -1) {
|
||||
breakpoints.remove(idx);
|
||||
} else {
|
||||
System.out.println("No breakpoint at " + addr);
|
||||
}
|
||||
}
|
||||
} else if (command.equalsIgnoreCase("n")
|
||||
|| command.equalsIgnoreCase("next")) {
|
||||
if (status == running) {
|
||||
interpretOneOperation();
|
||||
} else {
|
||||
System.out.println("Program is not running");
|
||||
}
|
||||
} else if (command.equalsIgnoreCase("c")
|
||||
|| command.equalsIgnoreCase("continue")) {
|
||||
continueProgram();
|
||||
} else if (command.equalsIgnoreCase("r")
|
||||
|| command.equalsIgnoreCase("run")) {
|
||||
runProgramFromStart();
|
||||
} else if (command.equalsIgnoreCase("i")
|
||||
|| command.equalsIgnoreCase("info")) {
|
||||
System.out.println("Breakpoints:");
|
||||
for (int b : breakpoints) {
|
||||
System.out.println("\t" + sourceLines.get(b));
|
||||
}
|
||||
} else if (command.equalsIgnoreCase("q")
|
||||
|| command.equalsIgnoreCase("quit")) {
|
||||
scanner.close();
|
||||
return;
|
||||
} else {
|
||||
System.out.println("Unknown command '" + command
|
||||
+ "'. Type 'help' for a list of commands");
|
||||
}
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
// RUNNING
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out
|
||||
.println("********** mJAM Interpreter (Version 1.2) **********");
|
||||
|
||||
String objectFileName;
|
||||
if (args.length >= 1)
|
||||
objectFileName = args[0];
|
||||
else
|
||||
objectFileName = "obj.mJAM";
|
||||
|
||||
String sourceFileName;
|
||||
if (args.length >= 2) {
|
||||
sourceFileName = args[1];
|
||||
debug(objectFileName, sourceFileName);
|
||||
} else {
|
||||
interpret(objectFileName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void interpret(String objectFileName) {
|
||||
|
||||
ObjectFile objectFile = new ObjectFile(objectFileName);
|
||||
if (objectFile.read()) {
|
||||
System.out.println("Unable to load object file " + objectFileName);
|
||||
return;
|
||||
}
|
||||
interpretProgram();
|
||||
showStatus();
|
||||
}
|
||||
|
||||
public static void debug(String objectFileName, String sourceFileName) {
|
||||
ObjectFile objectFile = new ObjectFile(objectFileName);
|
||||
if (objectFile.read()) {
|
||||
System.out.println("Unable to load object file " + objectFileName);
|
||||
return;
|
||||
}
|
||||
|
||||
sourceLines = new ArrayList<String>();
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(new File(
|
||||
sourceFileName)));
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
sourceLines.add(line);
|
||||
line = reader.readLine();
|
||||
}
|
||||
reader.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("Unable to load source file " + sourceFileName);
|
||||
return;
|
||||
} catch (IOException ie) {
|
||||
System.out.println("Unable to load source file " + sourceFileName);
|
||||
return;
|
||||
}
|
||||
|
||||
debugProgram();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
package mJAM;
|
||||
|
||||
/**
|
||||
* Defines names and sizes of mJAM instructions and primitives
|
||||
* @author prins
|
||||
* @version COMP 520 V2.2
|
||||
*/
|
||||
public final class Machine {
|
||||
|
||||
/**
|
||||
* mJAM instructions
|
||||
*/
|
||||
public enum Op {
|
||||
LOAD,
|
||||
LOADA,
|
||||
LOADI,
|
||||
LOADL,
|
||||
STORE,
|
||||
STOREI,
|
||||
CALL, // direct call of instance method
|
||||
CALLI, // indirect call of instance method
|
||||
CALLD, // dynamic call of instance method
|
||||
RETURN,
|
||||
PUSH,
|
||||
POP,
|
||||
JUMP,
|
||||
JUMPI,
|
||||
JUMPIF,
|
||||
HALT;
|
||||
}
|
||||
public static Op [] intToOp = Op.values();
|
||||
|
||||
|
||||
/**
|
||||
* mJAM registers
|
||||
*/
|
||||
public enum Reg {
|
||||
ZR, // zero, not used
|
||||
CB, // code base
|
||||
CT, // code top
|
||||
CP, // code pointer
|
||||
PB, // primitives base
|
||||
PT, // primitives top
|
||||
SB, // execution stack base
|
||||
ST, // execution stack top
|
||||
LB, // locals base
|
||||
HB, // heap base
|
||||
HT, // heap top
|
||||
OB; // object base
|
||||
}
|
||||
public static Reg [] intToReg = Reg.values();
|
||||
|
||||
/**
|
||||
* mJAM primitives
|
||||
*/
|
||||
public enum Prim {
|
||||
id,
|
||||
not,
|
||||
and,
|
||||
or,
|
||||
succ,
|
||||
pred,
|
||||
neg,
|
||||
add,
|
||||
sub,
|
||||
mult,
|
||||
div,
|
||||
mod,
|
||||
lt,
|
||||
le,
|
||||
ge,
|
||||
gt,
|
||||
eq,
|
||||
ne,
|
||||
eol,
|
||||
eof,
|
||||
get,
|
||||
put,
|
||||
geteol,
|
||||
puteol,
|
||||
getint,
|
||||
putint,
|
||||
putintnl,
|
||||
alloc,
|
||||
dispose,
|
||||
newobj,
|
||||
newarr,
|
||||
arrayref,
|
||||
arrayupd,
|
||||
fieldref,
|
||||
fieldupd;
|
||||
}
|
||||
public static Prim [] intToPrim = Prim.values();
|
||||
|
||||
|
||||
|
||||
// range for int constants
|
||||
public final static long
|
||||
minintRep = -2147483648,
|
||||
maxintRep = 2147483647;
|
||||
|
||||
|
||||
// CODE STORE REGISTERS
|
||||
public final static int CB = 0; // start of code space
|
||||
public final static int PB = 1024; // size of code space reserved for instructions
|
||||
public final static int PT = PB + Prim.values().length; // code space reserved for primitives
|
||||
|
||||
// CODE STORE
|
||||
public static Instruction[] code = new Instruction[PB];
|
||||
public static int CT = CB;
|
||||
|
||||
public static void initCodeGen() {
|
||||
CT = CB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places an instruction, with the given fields, into the next position in the code store
|
||||
* @param op - operation
|
||||
* @param n - length
|
||||
* @param r - register
|
||||
* @param d - displacement
|
||||
*/
|
||||
public static void emit(Op op, int n, Reg r, Prim d) {
|
||||
emit(op.ordinal(), n, r.ordinal(), d.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* emit operation with single literal argument d (n,r not used). These are
|
||||
* operations like LOADL 44, PUSH 3, and CALLD 1
|
||||
*/
|
||||
public static void emit(Op op, int d) {
|
||||
emit(op.ordinal(), 0, 0, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* emit "call primitive operation" (operation built-in to mJAM). This
|
||||
* generates CALL primitiveop[PB]
|
||||
*/
|
||||
public static void emit(Prim d) {
|
||||
emit(Op.CALL.ordinal(), 0, Machine.Reg.PB.ordinal(), d.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* emit operations without arguments. These are operations like
|
||||
* LOADI and STOREI
|
||||
*/
|
||||
public static void emit(Op op) {
|
||||
emit(op, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* emit operation with register r and integer displacement. These are
|
||||
* operations like JUMP 25[CB] and LOAD 6[LB]
|
||||
*/
|
||||
public static void emit(Op op, Reg r, int d) {
|
||||
emit(op.ordinal(), 0, r.ordinal(), d);
|
||||
}
|
||||
|
||||
/**
|
||||
* emit operation with n field, and register r and integer displacement. These are
|
||||
* operations like JUMPIF (1) 25[CB]. In the assembly code the value of n is shown
|
||||
* in parens.
|
||||
*/
|
||||
public static void emit(Op op, int n, Reg r, int d) {
|
||||
emit(op.ordinal(), n, r.ordinal(), d);
|
||||
}
|
||||
|
||||
/**
|
||||
* emit operation with integer n, r, d. These are operations
|
||||
* like RETURN (1) 3 and HALT (4) 0. For RETURN the value
|
||||
* of d is the number of caller args to pop off the callers
|
||||
* stack and n is the number of values to return at caller stack
|
||||
* top. n must be 0 or 1.
|
||||
*/
|
||||
public static void emit(Op op, int n, int r, int d) {
|
||||
emit(op.ordinal(), n, r, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* helper operation for emit using integer values
|
||||
*/
|
||||
private static void emit (int op, int n, int r, int d) {
|
||||
if (n > 255) {
|
||||
System.out.println("length of operand can't exceed 255 words");
|
||||
n = 255; // to allow code generation to continue
|
||||
}
|
||||
if (CT >= Machine.PB)
|
||||
System.out.println("mJAM: code segment capacity exceeded");
|
||||
|
||||
Instruction nextInstr = new Instruction(op, n, r, d);
|
||||
Machine.code[CT] = nextInstr;
|
||||
CT = CT + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return address (relative to CB) of next instruction to be generated
|
||||
*/
|
||||
public static int nextInstrAddr() {
|
||||
return CT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the displacement component of the (JUMP or CALL) instruction at addr
|
||||
* @param addr
|
||||
* @param displacement
|
||||
*/
|
||||
public static void patch(int addr, int displacement) {
|
||||
if (addr < 0 || addr >= CT) {
|
||||
System.out.println("patch: address of instruction to be patched is out of range");
|
||||
return;
|
||||
}
|
||||
if (displacement < 0 || displacement > CT) {
|
||||
System.out.println("patch: target address of patch is out of range");
|
||||
return;
|
||||
}
|
||||
Machine.code[addr].d = displacement;
|
||||
return;
|
||||
}
|
||||
|
||||
// DATA REPRESENTATION
|
||||
|
||||
public final static int
|
||||
booleanSize = 1,
|
||||
characterSize = 1,
|
||||
integerSize = 1,
|
||||
addressSize = 1,
|
||||
linkDataSize = 3 * addressSize, // caller's OB, LB, CP
|
||||
falseRep = 0,
|
||||
trueRep = 1,
|
||||
nullRep = 0;
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Reads and writes mJAM object files
|
||||
* @author prins
|
||||
* @version COMP 520 V2.2
|
||||
*/
|
||||
package mJAM;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
|
||||
public class ObjectFile {
|
||||
|
||||
String objectFileName;
|
||||
|
||||
public ObjectFile(String objectFileName) {
|
||||
super();
|
||||
this.objectFileName = objectFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write code store as object file
|
||||
* @param output object file
|
||||
* @return true if write fails
|
||||
*/
|
||||
public boolean write(){
|
||||
boolean failed = false;
|
||||
try {
|
||||
FileOutputStream objectFile = new FileOutputStream(objectFileName);
|
||||
DataOutputStream is = new DataOutputStream(objectFile);
|
||||
for (int i = Machine.CB; i < Machine.CT; i++ ){
|
||||
Instruction inst = Machine.code[i];
|
||||
is.writeInt(inst.op);
|
||||
is.writeInt(inst.n);
|
||||
is.writeInt(inst.r);
|
||||
is.writeInt(inst.d);
|
||||
}
|
||||
objectFile.close();
|
||||
}
|
||||
catch (Exception e) {failed = true;}
|
||||
return failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read object file into code store, setting CT
|
||||
* @return true if object code read fails
|
||||
*/
|
||||
public boolean read() {
|
||||
boolean failed = false;
|
||||
try {
|
||||
FileInputStream objectFile = new FileInputStream(objectFileName);
|
||||
DataInputStream is = new DataInputStream(objectFile);
|
||||
|
||||
Machine.CT = Machine.CB;
|
||||
while (is.available() > 0 && Machine.CT < Machine.PB){
|
||||
Instruction inst = new Instruction();
|
||||
inst.op = is.readInt();
|
||||
inst.n = is.readInt();
|
||||
inst.r = is.readInt();
|
||||
inst.d = is.readInt();
|
||||
Machine.code[Machine.CT++] = inst;
|
||||
}
|
||||
objectFile.close();
|
||||
} catch (Exception e) {
|
||||
failed = true;
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/**
|
||||
* Example illustrating components of mJAM package
|
||||
* @author prins
|
||||
* @version COMP 520 V2.2
|
||||
*/
|
||||
package mJAM;
|
||||
import mJAM.Machine.Op;
|
||||
import mJAM.Machine.Reg;
|
||||
import mJAM.Machine.Prim;
|
||||
|
||||
// test class to construct and run an mJAM program
|
||||
public class Test
|
||||
{
|
||||
public static void main(String[] args){
|
||||
|
||||
Machine.initCodeGen();
|
||||
System.out.println("Generating test program object code");
|
||||
|
||||
/* class A {
|
||||
* int x;
|
||||
* int p(){return x;}
|
||||
* }
|
||||
*/
|
||||
Machine.emit(Op.LOADL,11); // hello
|
||||
Machine.emit(Prim.putintnl);
|
||||
int patchme_coA = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP,Reg.CB,0); // jump around methods of class A (branch to /*coA*/)
|
||||
|
||||
// code for p() in A
|
||||
int label_pA = Machine.nextInstrAddr();
|
||||
/*pA*/ Machine.emit(Op.LOAD,Reg.OB,0); // x at offset 0 in current instance of A
|
||||
Machine.emit(Op.HALT,4,0,0);
|
||||
Machine.emit(Op.RETURN,1,0,0); // return one value, pop zero args
|
||||
|
||||
// build class object for A at 0[SB]
|
||||
int label_coA = Machine.nextInstrAddr();
|
||||
Machine.patch(patchme_coA, label_coA);
|
||||
/*coA*/ Machine.emit(Op.LOADL,-1); // no superclass object
|
||||
Machine.emit(Op.LOADL,1); // number of methods
|
||||
Machine.emit(Op.LOADA,Reg.CB,label_pA); // code addr of p_A
|
||||
|
||||
|
||||
/* class B extends A {
|
||||
* int y;
|
||||
* int p(){return x + 22;}
|
||||
* }
|
||||
*/
|
||||
int patchme_coB = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP,Reg.CB,0); // branch around methods in class B
|
||||
|
||||
// code for p() in B
|
||||
int label_pB = Machine.nextInstrAddr();
|
||||
/*pB*/ Machine.emit(Op.LOAD,Reg.OB,0); // x at offset 0 in current instance
|
||||
Machine.emit(Op.LOADL,22);
|
||||
Machine.emit(Op.HALT,4,0,0);
|
||||
Machine.emit(Prim.add);
|
||||
Machine.emit(Op.RETURN,1,0,0); // return one value, pop zero args
|
||||
|
||||
// build class object for B at 3[SB]
|
||||
int label_coB = Machine.nextInstrAddr();
|
||||
Machine.patch(patchme_coB, label_coB);
|
||||
/*coB*/ Machine.emit(Op.LOADA,Reg.SB,0); // addr of superclass object
|
||||
Machine.emit(Op.LOADL,1); // number of methods
|
||||
Machine.emit(Op.LOADA,Reg.CB,label_pB); // code addr of p_B
|
||||
|
||||
|
||||
/* class C {
|
||||
* public static void main(String [] args) {
|
||||
* A a = new A();
|
||||
* a.x = 33;
|
||||
* System.out.println(a.p());
|
||||
* ...
|
||||
*/
|
||||
int patchme_coC = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP,Reg.CB,0); // branch around methods of class C
|
||||
|
||||
// code for main() in C
|
||||
int label_mainC = Machine.nextInstrAddr();
|
||||
/*mainC*/ Machine.emit(Op.HALT,4,0,0);
|
||||
// local var "a" will be at 3[LB] after init
|
||||
Machine.emit(Op.LOADA,Reg.SB,0); // class descriptor for A
|
||||
Machine.emit(Op.LOADL,1); // size of A
|
||||
Machine.emit(Prim.newobj); // result addr becomes value of "a"
|
||||
Machine.emit(Op.LOAD,Reg.LB,3); // value of "a" (heap addr)
|
||||
Machine.emit(Op.LOADL,0); // "x" is field 0 in A
|
||||
Machine.emit(Op.LOADL,33); // new value 33
|
||||
Machine.emit(Op.HALT,4,0,0);
|
||||
Machine.emit(Prim.fieldupd); // a.x = 33
|
||||
Machine.emit(Op.LOAD,Reg.LB,3); // addr of instance "a" on heap
|
||||
Machine.emit(Op.CALLI,Reg.CB,label_pA); // call to known instance method p_A
|
||||
Machine.emit(Prim.putintnl); // print result
|
||||
|
||||
/* ...
|
||||
* A b = new B();
|
||||
* b.x = 44;
|
||||
* System.out.println(b.p());
|
||||
* } // end main
|
||||
* } // end class C
|
||||
*/
|
||||
// local var "b" will be at 4[LB] after init
|
||||
Machine.emit(Op.LOADA,Reg.SB,3); // class descriptor for B
|
||||
Machine.emit(Op.LOADL,2); // size of B
|
||||
Machine.emit(Prim.newobj); // result addr becomes value of "b"
|
||||
Machine.emit(Op.LOAD,Reg.LB,4); // fetch b
|
||||
Machine.emit(Op.LOADL,0); // field 0
|
||||
Machine.emit(Op.LOADL,44); // b.x = 44
|
||||
Machine.emit(Prim.fieldupd);
|
||||
Machine.emit(Op.HALT,4,0,0);
|
||||
Machine.emit(Op.LOAD,Reg.LB,4); // addr of instance "b"
|
||||
Machine.emit(Op.CALLD,0); // dynamic call, method index 0 (= method p)
|
||||
Machine.emit(Prim.putintnl); // print result
|
||||
Machine.emit(Op.RETURN,0,0,1); // return no value (void), pop 1 arg (= String [] args)
|
||||
|
||||
// build class descriptor for C at 6[SB]
|
||||
int label_coC = Machine.nextInstrAddr();
|
||||
Machine.patch(patchme_coC, label_coC);
|
||||
/*coC*/ Machine.emit(Op.LOADL,-1); // no superclass object
|
||||
Machine.emit(Op.LOADL,0); // number of methods = 0
|
||||
|
||||
/*
|
||||
* End of class declarations - call main
|
||||
*/
|
||||
Machine.emit(Op.LOADL,Machine.nullRep); // put null on stack as value of main's arg
|
||||
Machine.emit(Op.CALL,Reg.CB,label_mainC); // call known static main()
|
||||
Machine.emit(Op.LOADL,88); // goodbye
|
||||
Machine.emit(Prim.putintnl);
|
||||
Machine.emit(Machine.Op.HALT,0,0,0); // halt
|
||||
|
||||
/* write code as an object file */
|
||||
String objectCodeFileName = "test.mJAM";
|
||||
ObjectFile objF = new ObjectFile(objectCodeFileName);
|
||||
System.out.print("Writing object code file " + objectCodeFileName + " ... ");
|
||||
if (objF.write()) {
|
||||
System.out.println("FAILED!");
|
||||
return;
|
||||
}
|
||||
else
|
||||
System.out.println("SUCCEEDED");
|
||||
|
||||
/* create asm file using disassembler */
|
||||
String asmCodeFileName = "test.asm";
|
||||
System.out.print("Writing assembly file ... ");
|
||||
Disassembler d = new Disassembler(objectCodeFileName);
|
||||
if (d.disassemble()) {
|
||||
System.out.println("FAILED!");
|
||||
return;
|
||||
}
|
||||
else
|
||||
System.out.println("SUCCEEDED");
|
||||
|
||||
/* run code */
|
||||
System.out.println("Running code ... ");
|
||||
Interpreter.debug(objectCodeFileName, asmCodeFileName);
|
||||
// Interpreter.interpret(objectCodeFileName);
|
||||
|
||||
System.out.println("*** mJAM execution completed");
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package miniJava.AbstractSyntaxTrees;
|
||||
|
||||
import miniJava.CodeGenerator.RuntimeEntity;
|
||||
import miniJava.SyntacticAnalyzer.SourcePosition;
|
||||
|
||||
public abstract class Declaration extends AST {
|
||||
|
@ -25,6 +26,7 @@ public abstract class Declaration extends AST {
|
|||
}
|
||||
}
|
||||
|
||||
public RuntimeEntity entity;
|
||||
public String name;
|
||||
public Type type;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package miniJava.AbstractSyntaxTrees;
|
||||
|
||||
import miniJava.CodeGenerator.RuntimeEntity;
|
||||
import miniJava.SyntacticAnalyzer.SourcePosition;
|
||||
|
||||
public abstract class Reference extends AST {
|
||||
|
@ -13,4 +14,5 @@ public abstract class Reference extends AST {
|
|||
}
|
||||
|
||||
public Declaration decl;
|
||||
public RuntimeEntity entity;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,698 @@
|
|||
package miniJava.CodeGenerator;
|
||||
|
||||
import mJAM.Machine;
|
||||
import mJAM.Machine.*;
|
||||
import miniJava.AbstractSyntaxTrees.*;
|
||||
import miniJava.AbstractSyntaxTrees.Package;
|
||||
import miniJava.ContextualAnalyzer.Analyzer;
|
||||
|
||||
public class Encoder implements Visitor<Integer, Integer> {
|
||||
|
||||
private int mdLBOffset = 0;
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Convenience Functions
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Get size of type for declaration purposes.
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
private int getSize(Type t) {
|
||||
switch(t.typeKind) {
|
||||
case ARRAY:
|
||||
case CLASS:
|
||||
return Machine.addressSize;
|
||||
case INT:
|
||||
return Machine.integerSize;
|
||||
case BOOLEAN:
|
||||
return Machine.booleanSize;
|
||||
case VOID:
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PACKAGE
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Integer visitPackage(Package prog, Integer arg) {
|
||||
|
||||
// Initialize static fields
|
||||
for(ClassDecl cd : prog.classDeclList) {
|
||||
for(int i = 0; i < cd.fieldDeclList.size(); i++) {
|
||||
if(cd.fieldDeclList.get(i).isStatic) {
|
||||
cd.fieldDeclList.get(i).visit(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int patch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
|
||||
// Initialize all classes and methods
|
||||
for(ClassDecl cd : prog.classDeclList) {
|
||||
cd.visit(this, 1);
|
||||
}
|
||||
|
||||
// Link classes & build stack
|
||||
Machine.patch(patch, Machine.nextInstrAddr());
|
||||
for(ClassDecl cd : prog.classDeclList) {
|
||||
int cdPatch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
Machine.patch(cdPatch, cd.visit(this, 0));
|
||||
}
|
||||
|
||||
// Build main function
|
||||
mdLBOffset = Machine.linkDataSize;
|
||||
int mainPatch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
Analyzer.mainMethod.visit(this, 0);
|
||||
|
||||
|
||||
// Run main function
|
||||
int mainLabel = Machine.nextInstrAddr();
|
||||
RuntimeEntity main = Analyzer.mainMethod.entity;
|
||||
|
||||
Machine.emit(Op.LOADL, Machine.nullRep);
|
||||
Machine.emit(Op.CALL, Reg.CB, main.addr);
|
||||
Machine.emit(Machine.Op.HALT, 0 ,0, 0);
|
||||
Machine.patch(mainPatch, mainLabel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DECLARATIONS
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Integer visitClassDecl(ClassDecl cd, Integer init) {
|
||||
|
||||
/* Give an address to each method and class, so it can be referred
|
||||
* to directly by jumping.
|
||||
*/
|
||||
if(init != 0) {
|
||||
|
||||
// Get size of class
|
||||
int size = 0;
|
||||
for(FieldDecl fd : cd.fieldDeclList) {
|
||||
if(!fd.isStatic) {
|
||||
fd.visit(this, size);
|
||||
size += getSize(fd.type);
|
||||
}
|
||||
}
|
||||
|
||||
// Build Methods
|
||||
for(MethodDecl md : cd.methodDeclList) {
|
||||
md.visit(this, init);
|
||||
}
|
||||
|
||||
// Runtime Entity
|
||||
int addr = Machine.nextInstrAddr();
|
||||
cd.entity = new RuntimeEntity(size, addr, Reg.CB, addr);
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
// Build Instance Methods
|
||||
for(MethodDecl md : cd.methodDeclList) {
|
||||
if(!md.isStatic) {
|
||||
md.visit(this, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Build class descriptor
|
||||
int label = Machine.nextInstrAddr();
|
||||
Machine.patch(cd.entity.instr, label);
|
||||
|
||||
Machine.emit(Op.LOADL, -1); // No superclasses allowed
|
||||
Machine.emit(Op.LOADL, cd.entity.size); // Size of class
|
||||
Machine.emit(Op.LOADA, Reg.CB, cd.entity.addr); // Address of class
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitFieldDecl(FieldDecl fd, Integer addr) {
|
||||
|
||||
int size = getSize(fd.type);
|
||||
|
||||
// Static fields are placed onto the stack
|
||||
if(fd.isStatic) {
|
||||
fd.entity = new RuntimeEntity(size, addr, Reg.SB);
|
||||
Machine.emit(Op.PUSH, size);
|
||||
} else {
|
||||
fd.entity = new RuntimeEntity(size, addr, Reg.OB);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Integer visitMethodDecl(MethodDecl md, Integer init) {
|
||||
|
||||
mdLBOffset = Machine.linkDataSize;
|
||||
|
||||
if(init != 0) {
|
||||
int size = getSize(md.type);
|
||||
int addr = Machine.nextInstrAddr();
|
||||
md.entity = new RuntimeEntity(size, addr, Reg.CB, addr);
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
}
|
||||
|
||||
else {
|
||||
// Setup parameters
|
||||
int paramS = 0;
|
||||
for(ParameterDecl pd : md.parameterDeclList) {
|
||||
paramS += getSize(md.type);
|
||||
pd.visit(this, -paramS);
|
||||
}
|
||||
|
||||
// Setup body
|
||||
int addr = Machine.nextInstrAddr();
|
||||
Machine.patch(md.entity.instr, addr);
|
||||
for(Statement s : md.statementList) {
|
||||
s.visit(this, 0);
|
||||
}
|
||||
|
||||
// Setup return statement
|
||||
if(md.returnExp != null) {
|
||||
md.returnExp.visit(this, 0);
|
||||
}
|
||||
|
||||
// Return from function
|
||||
// Machine.emit(Op.HALT, 4, 0, 0); (See snapshot)
|
||||
Machine.emit(Op.RETURN, md.entity.size, 0, paramS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note parameter addresses are negative to LB since they are
|
||||
* placed onto the stack before emitting a CALL.
|
||||
*/
|
||||
@Override
|
||||
public Integer visitParameterDecl(ParameterDecl pd, Integer addr) {
|
||||
|
||||
// Builds runtime entity, offset at LB
|
||||
int size = getSize(pd.type);
|
||||
pd.entity = new RuntimeEntity(size, addr, Reg.LB);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variable declarations are never disposed (even in static closures).
|
||||
*/
|
||||
@Override
|
||||
public Integer visitVarDecl(VarDecl decl, Integer addr) {
|
||||
|
||||
// Builds runtime entity, offset at LB
|
||||
int size = getSize(decl.type);
|
||||
decl.entity = new RuntimeEntity(size, addr, Reg.LB);
|
||||
|
||||
// Allocates space on stack
|
||||
Machine.emit(Op.PUSH, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TYPES
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Integer visitBaseType(BaseType type, Integer arg) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitClassType(ClassType type, Integer arg) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitArrayType(ArrayType type, Integer arg) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// STATEMENTS
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* We note a block statement may contain a variable declaration,
|
||||
* that should go out of scope at the end of the statement.
|
||||
* If the passed argument is 1, we indicate we want this to
|
||||
* happen.
|
||||
*/
|
||||
@Override
|
||||
public Integer visitBlockStmt(BlockStmt stmt, Integer arg) {
|
||||
|
||||
// Execute statements
|
||||
int size = 0;
|
||||
for(Statement s : stmt.sl) {
|
||||
s.visit(this, 0);
|
||||
|
||||
// Push variable declarations if necessary
|
||||
if(arg == 1 && s instanceof VarDeclStmt) {
|
||||
VarDeclStmt decl = (VarDeclStmt) s;
|
||||
size += getSize(decl.varDecl.type);
|
||||
}
|
||||
}
|
||||
|
||||
// Pop off variable declarations
|
||||
Machine.emit(Op.POP, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* We declare the variable, place the RHS onto the stack, and
|
||||
* replace the value of the variable with the top of the stack.
|
||||
*/
|
||||
@Override
|
||||
public Integer visitVardeclStmt(VarDeclStmt stmt, Integer arg) {
|
||||
stmt.varDecl.visit(this, mdLBOffset);
|
||||
stmt.initExp.visit(this, 0);
|
||||
|
||||
// Assign value
|
||||
RuntimeEntity e = stmt.varDecl.entity;
|
||||
Machine.emit(Op.STORE, e.size, e.register, e.addr);
|
||||
|
||||
// Update position
|
||||
mdLBOffset += getSize(stmt.varDecl.type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitAssignStmt(AssignStmt stmt, Integer arg) {
|
||||
|
||||
stmt.ref.visit(this, 0);
|
||||
|
||||
// Setup
|
||||
RuntimeEntity e = stmt.ref.entity;
|
||||
|
||||
// Build code accordingly
|
||||
if(stmt.ref instanceof QualifiedRef) {
|
||||
e.parent.load();
|
||||
Machine.emit(Op.LOADL, e.addr);
|
||||
stmt.val.visit(this, 0);
|
||||
Machine.emit(Prim.fieldupd);
|
||||
}
|
||||
|
||||
else if(stmt.ref instanceof IndexedRef) {
|
||||
IndexedRef ref = (IndexedRef) stmt.ref;
|
||||
e.load();
|
||||
ref.indexExpr.visit(this, 0);
|
||||
stmt.val.visit(this, 0);
|
||||
Machine.emit(Prim.arrayupd);
|
||||
}
|
||||
|
||||
else if(stmt.ref instanceof IdRef) {
|
||||
stmt.val.visit(this, 0);
|
||||
Machine.emit(Op.STORE, e.size, e.register, e.addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method in question can either be an instance or static
|
||||
* function (it must be a MethodDecl).
|
||||
*/
|
||||
@Override
|
||||
public Integer visitCallStmt(CallStmt stmt, Integer arg) {
|
||||
|
||||
MethodDecl md = (MethodDecl) stmt.methodRef.decl;
|
||||
|
||||
// Request to print out
|
||||
if(md == Analyzer.println) {
|
||||
stmt.argList.get(0).visit(this, 0);
|
||||
Machine.emit(Prim.putintnl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Push parameters on (must iterate in reverse order)
|
||||
for(int i = stmt.argList.size() - 1; i >= 0; i--) {
|
||||
stmt.argList.get(i).visit(this, 0);
|
||||
}
|
||||
|
||||
// Call Method
|
||||
stmt.methodRef.visit(this, 0);
|
||||
if(md.isStatic) {
|
||||
Machine.emit(Op.CALL, Reg.CB, md.entity.addr);
|
||||
} else {
|
||||
stmt.methodRef.entity.call();
|
||||
Machine.emit(Op.CALLI, Reg.CB, md.entity.addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitIfStmt(IfStmt stmt, Integer arg) {
|
||||
|
||||
stmt.cond.visit(this, 0);
|
||||
|
||||
// Build Then Statement
|
||||
int ifPatch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMPIF, Machine.trueRep, Reg.CB, 0);
|
||||
|
||||
int elsePatch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
|
||||
int thenLabel = Machine.nextInstrAddr();
|
||||
stmt.thenStmt.visit(this, 0);
|
||||
|
||||
int thenPatch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMP, Reg.CB, 0);
|
||||
|
||||
// Connect labels/patches
|
||||
int endLabel = Machine.nextInstrAddr();
|
||||
Machine.patch(elsePatch, endLabel);
|
||||
|
||||
if(stmt.elseStmt != null) {
|
||||
stmt.elseStmt.visit(this, 0);
|
||||
endLabel = Machine.nextInstrAddr();
|
||||
}
|
||||
|
||||
Machine.patch(ifPatch, thenLabel);
|
||||
Machine.patch(thenPatch, endLabel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* We note since the same declaration can be reached multiple times, we
|
||||
* are forced to pop each declaration initialized.
|
||||
*/
|
||||
@Override
|
||||
public Integer visitWhileStmt(WhileStmt stmt, Integer arg) {
|
||||
|
||||
// Must check the condition each loop
|
||||
int whileLabel = Machine.nextInstrAddr();
|
||||
stmt.cond.visit(this, 0);
|
||||
|
||||
// Jump out once condition fails
|
||||
int whileEndPatch = Machine.nextInstrAddr();
|
||||
Machine.emit(Op.JUMPIF, Machine.falseRep, Reg.CB, 0);
|
||||
|
||||
// Execute
|
||||
stmt.body.visit(this, 1);
|
||||
Machine.emit(Op.JUMP, Reg.CB, whileLabel);
|
||||
Machine.patch(whileEndPatch, Machine.nextInstrAddr());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// EXPRESSIONS
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Integer visitUnaryExpr(UnaryExpr expr, Integer arg) {
|
||||
|
||||
expr.expr.visit(this, 0);
|
||||
switch(expr.operator.spelling) {
|
||||
case "!":
|
||||
Machine.emit(Prim.not);
|
||||
break;
|
||||
case "-":
|
||||
Machine.emit(Prim.neg);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitBinaryExpr(BinaryExpr expr, Integer arg) {
|
||||
expr.left.visit(this, 0);
|
||||
expr.right.visit(this, 0);
|
||||
expr.operator.visit(this, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitRefExpr(RefExpr expr, Integer arg) {
|
||||
|
||||
expr.ref.visit(this, 0);
|
||||
|
||||
// Build code accordingly
|
||||
if(expr.ref instanceof QualifiedRef) {
|
||||
|
||||
// Must be requesting length of array
|
||||
if(expr.ref.decl.type.typeKind == TypeKind.ARRAY) {
|
||||
int size = Machine.integerSize;
|
||||
int addr = expr.ref.entity.addr + size;
|
||||
Machine.emit(Op.LOAD, size, Reg.LB, addr);
|
||||
} else {
|
||||
expr.ref.entity.load();
|
||||
}
|
||||
}
|
||||
|
||||
else if(expr.ref instanceof IndexedRef) {
|
||||
IndexedRef ref = (IndexedRef) expr.ref;
|
||||
ref.entity.load();
|
||||
ref.indexExpr.visit(this, 0);
|
||||
Machine.emit(Prim.arrayref);
|
||||
}
|
||||
|
||||
else if(expr.ref instanceof ThisRef) {
|
||||
RuntimeEntity e = expr.ref.entity;
|
||||
Machine.emit(Op.LOADA, e.size, e.register, e.addr);
|
||||
}
|
||||
|
||||
else {
|
||||
expr.ref.entity.load();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitCallExpr(CallExpr expr, Integer arg) {
|
||||
|
||||
// Push parameters on (must iterate in reverse order)
|
||||
for(int i = expr.argList.size() - 1; i >= 0; i--) {
|
||||
expr.argList.get(i).visit(this, 0);
|
||||
}
|
||||
|
||||
// Call method
|
||||
MethodDecl md = (MethodDecl) expr.functionRef.decl;
|
||||
expr.functionRef.visit(this, 0);
|
||||
if(md.isStatic) {
|
||||
Machine.emit(Op.CALL, Reg.CB, md.entity.addr);
|
||||
} else {
|
||||
expr.functionRef.entity.call();
|
||||
Machine.emit(Op.CALLI, Reg.CB, md.entity.addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitLiteralExpr(LiteralExpr expr, Integer arg) {
|
||||
|
||||
expr.literal.visit(this, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitNewObjectExpr(NewObjectExpr expr, Integer arg) {
|
||||
|
||||
RuntimeEntity e = expr.classtype.className.decl.entity;
|
||||
Machine.emit(Op.LOADA, e.register, e.addr);
|
||||
Machine.emit(Op.LOADL, e.size);
|
||||
Machine.emit(Prim.newobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address where the new array's size is being
|
||||
* stored (as it cannot be easily accessed otherwise).
|
||||
*/
|
||||
@Override
|
||||
public Integer visitNewArrayExpr(NewArrayExpr expr, Integer arg) {
|
||||
|
||||
// Setup
|
||||
int addr = mdLBOffset;
|
||||
int size = Machine.integerSize;
|
||||
mdLBOffset += Machine.integerSize;
|
||||
|
||||
// Add to stack
|
||||
expr.sizeExpr.visit(this, 0);
|
||||
|
||||
// Create new array
|
||||
Machine.emit(Op.LOAD, size, Reg.LB, addr);
|
||||
Machine.emit(Prim.newarr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// REFERENCES
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Integer visitQualifiedRef(QualifiedRef ref, Integer arg) {
|
||||
|
||||
ref.ref.visit(this, 0);
|
||||
|
||||
// Must be accessing length of an array
|
||||
if(ref.ref.decl.type.typeKind == TypeKind.ARRAY) {
|
||||
ref.decl = ref.ref.decl;
|
||||
ref.entity = ref.ref.entity;
|
||||
}
|
||||
|
||||
// Access class member
|
||||
else {
|
||||
ref.entity = ref.id.decl.entity;
|
||||
ref.entity.parent = ref.ref.entity;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitIndexedRef(IndexedRef ref, Integer arg) {
|
||||
|
||||
ref.entity = ref.decl.entity;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitIdRef(IdRef ref, Integer arg) {
|
||||
|
||||
ref.entity = ref.decl.entity;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitThisRef(ThisRef ref, Integer arg) {
|
||||
|
||||
RuntimeEntity e = ref.decl.entity;
|
||||
ref.entity = new RuntimeEntity(e.size, 0, Reg.OB);
|
||||
ref.entity.indirect = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TERMINALS
|
||||
//
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public Integer visitIdentifier(Identifier id, Integer arg) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitOperator(Operator op, Integer arg) {
|
||||
|
||||
switch(op.token.spelling) {
|
||||
case "+":
|
||||
Machine.emit(Prim.add);
|
||||
break;
|
||||
case "-":
|
||||
Machine.emit(Prim.sub);
|
||||
break;
|
||||
case "*":
|
||||
Machine.emit(Prim.mult);
|
||||
break;
|
||||
case "/":
|
||||
Machine.emit(Prim.div);
|
||||
break;
|
||||
case "<":
|
||||
Machine.emit(Prim.lt);
|
||||
break;
|
||||
case ">":
|
||||
Machine.emit(Prim.gt);
|
||||
break;
|
||||
case "<=":
|
||||
Machine.emit(Prim.le);
|
||||
break;
|
||||
case ">=":
|
||||
Machine.emit(Prim.ge);
|
||||
break;
|
||||
case "==":
|
||||
Machine.emit(Prim.eq);
|
||||
break;
|
||||
case "!=":
|
||||
Machine.emit(Prim.ne);
|
||||
break;
|
||||
case "&&":
|
||||
Machine.emit(Prim.and);
|
||||
break;
|
||||
case "||":
|
||||
Machine.emit(Prim.or);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitIntLiteral(IntLiteral num, Integer arg) {
|
||||
|
||||
Integer lit = Integer.parseInt(num.spelling);
|
||||
Machine.emit(Op.LOADL, lit.intValue());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitBooleanLiteral(BooleanLiteral bool, Integer arg) {
|
||||
|
||||
if(bool.spelling.equals("true")) {
|
||||
Machine.emit(Op.LOADL, Machine.trueRep);
|
||||
} else {
|
||||
Machine.emit(Op.LOADL, Machine.falseRep);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package miniJava.CodeGenerator;
|
||||
|
||||
import mJAM.Machine;
|
||||
import mJAM.Machine.Op;
|
||||
import mJAM.Machine.Prim;
|
||||
import mJAM.Machine.Reg;
|
||||
|
||||
public class RuntimeEntity {
|
||||
|
||||
public int size = -1; // Size of type
|
||||
public int addr = -1; // Position relative to register
|
||||
public int instr = -1; // Instruction relative to CB
|
||||
public Reg register = Reg.ZR; // Register to offset by
|
||||
|
||||
// For use with nested elements
|
||||
public boolean indirect = false;
|
||||
public RuntimeEntity parent = null;
|
||||
|
||||
public RuntimeEntity(int size, int addr, Reg register) {
|
||||
this(size, addr, register, -1);
|
||||
}
|
||||
|
||||
public RuntimeEntity(int size, int addr, Reg register, int instr) {
|
||||
this.size = size;
|
||||
this.addr = addr;
|
||||
this.register = register;
|
||||
this.instr = instr;
|
||||
}
|
||||
|
||||
// Load entity into memory (if this, should call LOADA)
|
||||
public void load() {
|
||||
if(parent != null) {
|
||||
parent.load();
|
||||
Machine.emit(Op.LOADL, addr);
|
||||
Machine.emit(Prim.fieldref);
|
||||
} else {
|
||||
if(indirect) {
|
||||
Machine.emit(Op.LOADA, size, register, addr);
|
||||
} else {
|
||||
Machine.emit(Op.LOAD, size, register, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void call() {
|
||||
if(parent != null) {
|
||||
parent.load();
|
||||
} else {
|
||||
Machine.emit(Op.LOADA, size, Reg.OB, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,10 +2,11 @@ package miniJava;
|
|||
|
||||
import java.io.*;
|
||||
|
||||
import mJAM.*;
|
||||
import miniJava.SyntacticAnalyzer.*;
|
||||
// import miniJava.AbstractSyntaxTrees.ASTDisplay;
|
||||
import miniJava.AbstractSyntaxTrees.Package;
|
||||
import miniJava.ContextualAnalyzer.Analyzer;
|
||||
import miniJava.CodeGenerator.Encoder;
|
||||
import miniJava.Exceptions.*;
|
||||
|
||||
public class Compiler {
|
||||
|
@ -25,15 +26,28 @@ public class Compiler {
|
|||
Scanner scanner = new Scanner(new BufferedReader(input));
|
||||
Parser parser = new Parser(scanner);
|
||||
Package p = parser.parse();
|
||||
|
||||
// Display
|
||||
// ASTDisplay display = new ASTDisplay();
|
||||
// display.showTree(p);
|
||||
|
||||
// Identification/Type Checking
|
||||
Analyzer analyzer = new Analyzer();
|
||||
analyzer.visitPackage(p, null);
|
||||
System.exit(analyzer.validate());
|
||||
int analyzed = analyzer.validate();
|
||||
|
||||
// Begin Compilation to mJAM
|
||||
if(analyzed == 0) {
|
||||
|
||||
Encoder encoder = new Encoder();
|
||||
encoder.visitPackage(p, null);
|
||||
|
||||
// Create object file
|
||||
int pos = args[0].lastIndexOf(".java");
|
||||
String objectFileName = args[0].substring(0, pos) + ".mJAM";
|
||||
ObjectFile objF = new ObjectFile(objectFileName);
|
||||
if(objF.write()) {
|
||||
System.out.println("***Object File Failed.");
|
||||
}
|
||||
}
|
||||
|
||||
System.exit(analyzed);
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("***" + e.getMessage());
|
||||
|
|
|
@ -11,10 +11,18 @@ import miniJava.AbstractSyntaxTrees.Package;
|
|||
|
||||
public class Analyzer implements Visitor<IdentificationTable, Type> {
|
||||
|
||||
private MethodDecl mainMethod = null;
|
||||
public static MethodDecl mainMethod = null;
|
||||
public static MethodDecl println = null;
|
||||
|
||||
// Notifies if handling predefined as opposed to user defined code
|
||||
private boolean predefinedGen = true;
|
||||
|
||||
// Keeps track of declarations
|
||||
private VarDecl currentVarDecl = null;
|
||||
private ClassDecl currentClassDecl = null;
|
||||
private MethodDecl currentMethodDecl = null;
|
||||
private VarDecl currentVarDecl = null;
|
||||
|
||||
// Keeps track of identifcatoin
|
||||
private IdentificationTable table = new IdentificationTable();
|
||||
|
||||
// Keep track of all predefined names to handle
|
||||
|
@ -38,6 +46,7 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
|||
Parser parser = new Parser(scanner);
|
||||
parser.parse().visit(this, table);
|
||||
}
|
||||
predefinedGen = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,6 +143,10 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
|||
}
|
||||
|
||||
public Type visitMethodDecl(MethodDecl md, IdentificationTable arg) {
|
||||
|
||||
if(predefinedGen && md.name.equals("println")) {
|
||||
println = md;
|
||||
}
|
||||
|
||||
// Check if a valid entry point to program
|
||||
if (IdentificationTable.isMainMethod(md)) {
|
||||
|
@ -420,8 +433,18 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
|||
|
||||
Type refType = ref.ref.visit(this, arg);
|
||||
|
||||
// Array types only have the single 'length' field
|
||||
if(refType.typeKind == TypeKind.ARRAY) {
|
||||
if(!ref.id.spelling.equals("length")) {
|
||||
Reporter.report(ErrorType.LENGTH, ref.id, null);
|
||||
return refType;
|
||||
}
|
||||
|
||||
return new BaseType(TypeKind.INT, ref.id.posn);
|
||||
}
|
||||
|
||||
// Note qualified ref's only make sense in the context of classes
|
||||
if(refType.typeKind != TypeKind.CLASS) {
|
||||
else if(refType.typeKind != TypeKind.CLASS) {
|
||||
if(refType.typeKind != TypeKind.ERROR) // Don't need to report multiple times
|
||||
Reporter.report(ErrorType.TYPE_MISMATCH, new BaseType(TypeKind.CLASS, null), refType);
|
||||
|
||||
|
@ -446,7 +469,7 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
|||
Reporter.report(ErrorType.UNDEFINED, ref.id, null);
|
||||
return new BaseType(TypeKind.ERROR, ref.id.posn);
|
||||
}
|
||||
|
||||
|
||||
// If the qualifed ref is a class declaration, members must be static (must check for 'this')
|
||||
else if(qualified instanceof ClassDecl && !(ref.ref instanceof ThisRef)) {
|
||||
if(!md.isStatic) {
|
||||
|
@ -468,7 +491,7 @@ public class Analyzer implements Visitor<IdentificationTable, Type> {
|
|||
Reporter.report(ErrorType.VISIBILITY, md, ref.id);
|
||||
return new BaseType(TypeKind.ERROR, ref.id.posn);
|
||||
}
|
||||
|
||||
|
||||
ref.id.decl = md;
|
||||
ref.decl = md;
|
||||
return md.type;
|
||||
|
|
|
@ -4,6 +4,7 @@ import miniJava.AbstractSyntaxTrees.*;
|
|||
|
||||
enum ErrorType {
|
||||
THIS,
|
||||
LENGTH,
|
||||
VOID_TYPE,
|
||||
CLASS_IDENTIFER,
|
||||
VARDECL_USED,
|
||||
|
@ -72,9 +73,15 @@ public class Reporter {
|
|||
break;
|
||||
}
|
||||
|
||||
// Array types have the single field 'length'
|
||||
case LENGTH: {
|
||||
emit("Array types have only a single field 'length' (at " + a1.posn + ").");
|
||||
break;
|
||||
}
|
||||
|
||||
// Can't use a class as an identifier solely
|
||||
case CLASS_IDENTIFER: {
|
||||
emit("Cannot use class identifier by outside of a qualified reference at " + a1.posn);
|
||||
emit("Cannot use class identifier outside of a qualified reference at " + a1.posn);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue