1
Fork 0

Working compiler.

master
Joshua Potter 2014-04-26 13:23:14 -04:00
parent 2f673d60b7
commit 8390199866
80 changed files with 4009 additions and 3488 deletions

View File

@ -1,14 +1,19 @@
MiniJava Compiler
=================
[Version 1.0.0 - 04/15/2014]
[Version 1.0.0 - 04/26/2014]
The following is a LL(1) compiler for a subset of Java, denoted MiniJava. As of now, it appears to work for the most
part, passing the regression tests performed after each segment.
The following is a complete MiniJava Compiler implementation, according to the specifications in the docs folder.
Noted bugs:
* Changing a field of an object in an array causes crashes
This is not really a surprise, considering the Contextual Analysis aspect of the project
is in fairly rough shape.
As of now, I do not plan on implementing more things to this, unless there is some potential for extra credit.
Other Features to include in future releases
--------------------------------------------
* Static Field Initialization
* Keyword 'null'
* Support class constructors
* 'for' loop
* String type and string literals
* Code generation for conditional operations && and ||
* Overloaded methods
* Inheritance of fields and methods
* Dynamic Method Invocation
* Support for instanceof/super

Binary file not shown.

BIN
docs/pa-final.pdf Normal file

Binary file not shown.

BIN
docs/pa1-updated.pdf Normal file

Binary file not shown.

View File

@ -151,7 +151,6 @@ public class Test
/* run code */
System.out.println("Running code ... ");
Interpreter.debug(objectCodeFileName, asmCodeFileName);
// Interpreter.interpret(objectCodeFileName);
System.out.println("*** mJAM execution completed");
}

View File

@ -19,5 +19,9 @@ public class ArrayType extends Type {
return v.visitArrayType(this, o);
}
public String toString() {
return eltType + " Array";
}
public Type eltType;
}

View File

@ -15,4 +15,8 @@ public class BaseType extends Type {
public <A, R> R visit(Visitor<A, R> v, A o) {
return v.visitBaseType(this, o);
}
public String toString() {
return typeKind.toString();
}
}

View File

@ -9,13 +9,11 @@ import miniJava.SyntacticAnalyzer.SourcePosition;
public class ClassDecl extends Declaration {
public ClassDecl(String cn, FieldDeclList fdl, MethodDeclList mdl, SourcePosition posn) {
public ClassDecl(String cn, FieldDeclList fdl, MethodDeclList mdl,
SourcePosition posn) {
super(cn, null, posn);
fieldDeclList = fdl;
methodDeclList = mdl;
Identifier ident = new Identifier(cn, posn);
type = new ClassType(ident, posn);
}
public <A, R> R visit(Visitor<A, R> v, A o) {

View File

@ -17,5 +17,9 @@ public class ClassType extends Type {
return v.visitClassType(this, o);
}
public String toString() {
return "Class " + className.spelling;
}
public Identifier className;
}

View File

@ -6,6 +6,7 @@
package miniJava.AbstractSyntaxTrees;
import miniJava.CodeGenerator.RuntimeEntity;
import miniJava.ContextualAnalyzer.IdTable;
import miniJava.SyntacticAnalyzer.SourcePosition;
public abstract class Declaration extends AST {
@ -16,17 +17,8 @@ public abstract class Declaration extends AST {
this.type = type;
}
@Override
public String toString() {
if (posn != null) {
return this.name + "(Line: " + posn.line + ", Column: " + posn.col
+ ")";
} else {
return super.toString();
}
}
public RuntimeEntity entity;
public IdTable table;
public String name;
public Type type;
}

View File

@ -2,6 +2,10 @@ package miniJava.AbstractSyntaxTrees;
import miniJava.SyntacticAnalyzer.SourcePosition;
/**
* The following is a custom class, included for use with method and field
* declarations.
*/
public class Declarators {
public Declarators(boolean isPrivate, boolean isStatic, Type mt,

View File

@ -18,6 +18,10 @@ public class FieldDecl extends MemberDecl {
super(md, posn);
}
public FieldDecl(Declarators d, String name) {
super(d.isPrivate, d.isStatic, d.mt, name, d.posn);
}
public <A, R> R visit(Visitor<A, R> v, A o) {
return v.visitFieldDecl(this, o);
}

View File

@ -12,11 +12,6 @@ public class FieldDeclList implements Iterable<FieldDecl> {
fieldDeclList = new ArrayList<FieldDecl>();
}
public FieldDeclList(FieldDecl f) {
fieldDeclList = new ArrayList<FieldDecl>();
fieldDeclList.add(f);
}
public void add(FieldDecl cd) {
fieldDeclList.add(cd);
}

View File

@ -5,6 +5,7 @@
*/
package miniJava.AbstractSyntaxTrees;
import miniJava.CodeGenerator.RuntimeEntity;
import miniJava.SyntacticAnalyzer.SourcePosition;
public class Identifier extends Terminal {
@ -17,5 +18,6 @@ public class Identifier extends Terminal {
return v.visitIdentifier(this, o);
}
public Declaration decl = null;
public Declaration decl;
public RuntimeEntity entity;
}

View File

@ -12,11 +12,6 @@ public class MethodDeclList implements Iterable<MethodDecl> {
methodDeclList = new ArrayList<MethodDecl>();
}
public MethodDeclList(MethodDecl m) {
methodDeclList = new ArrayList<MethodDecl>();
methodDeclList.add(m);
}
public void add(MethodDecl cd) {
methodDeclList.add(cd);
}

View File

@ -12,7 +12,6 @@ public class Operator extends Terminal {
public Operator(Token t, SourcePosition posn) {
super(t.spelling, posn);
token = t;
}
public <A, R> R visit(Visitor<A, R> v, A o) {

View File

@ -12,11 +12,6 @@ public class ParameterDeclList implements Iterable<ParameterDecl> {
parameterDeclList = new ArrayList<ParameterDecl>();
}
public ParameterDeclList(ParameterDecl p) {
parameterDeclList = new ArrayList<ParameterDecl>();
parameterDeclList.add(p);
}
public void add(ParameterDecl s) {
parameterDeclList.add(s);
}

View File

@ -13,6 +13,7 @@ public abstract class Reference extends AST {
super(posn);
}
public String spelling;
public Declaration decl;
public RuntimeEntity entity;
}

View File

@ -6,5 +6,5 @@
package miniJava.AbstractSyntaxTrees;
public enum TypeKind {
VOID, INT, BOOLEAN, CLASS, ARRAY, UNSUPPORTED, ERROR, EQUALS, RELATIONAL;
VOID, INT, BOOLEAN, CLASS, ARRAY, UNSUPPORTED, ERROR;
}

View File

@ -0,0 +1,55 @@
package miniJava.CodeGenerator;
import mJAM.Machine;
import miniJava.AbstractSyntaxTrees.*;
public class Code {
public boolean addr;
public Declaration decl;
/**
* If addr true, returns the address of the declaration.
* Otherwise, uses the size of the declaration in its place.
* @param op
* @param decl
* @param addr
*/
public Code(Declaration decl, boolean addr) {
this.decl = decl;
this.addr = addr;
}
/**
*
* @param index
*/
public void modify(int instr) {
// Setup size
switch(decl.type.typeKind) {
case ARRAY:
case CLASS:
Machine.code[instr].n = Machine.addressSize;
case INT:
Machine.code[instr].n = Machine.integerSize;
case BOOLEAN:
Machine.code[instr].n = Machine.booleanSize;
case VOID:
Machine.code[instr].n = 0;
default:
Machine.code[instr].n = -1;
}
// Setup displacement
if(addr) {
Machine.code[instr].d += decl.entity.addr;
} else {
Machine.code[instr].d += decl.entity.size;
}
// Setup register
Machine.code[instr].r = decl.entity.reg.ordinal();
}
}

View File

@ -1,41 +1,20 @@
package miniJava.CodeGenerator;
import java.util.HashMap;
import mJAM.Machine;
import mJAM.Machine.*;
import miniJava.AbstractSyntaxTrees.*;
import miniJava.AbstractSyntaxTrees.Package;
import miniJava.ContextualAnalyzer.Analyzer;
public class Encoder implements Visitor<Integer, Integer> {
public class Encoder implements Visitor<Integer, Object> {
private int mdLBOffset = 0;
// Keeps track of variables placed on the stack
private int methodDataOffset = 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;
}
}
// Maintains data to correct once completed traversal
private HashMap<Integer, Code> patches = new HashMap<>();
// /////////////////////////////////////////////////////////////////////////////
@ -45,50 +24,56 @@ public class Encoder implements Visitor<Integer, Integer> {
// /////////////////////////////////////////////////////////////////////////////
@Override
public Integer visitPackage(Package prog, Integer arg) {
public Object visitPackage(Package prog, Integer arg) {
// Initialize static fields
Machine.initCodeGen();
// Initialize all static fields
int staticFieldsSize = 0;
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);
FieldDecl fd = cd.fieldDeclList.get(i);
if(fd.isStatic) {
int size = getSize(fd.type);
fd.entity = new RuntimeEntity(size, staticFieldsSize, Reg.SB);
staticFieldsSize += size;
}
}
}
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);
if(staticFieldsSize > 0) {
Machine.emit(Op.PUSH, staticFieldsSize);
}
// Link classes & build stack
Machine.patch(patch, Machine.nextInstrAddr());
// Build Classes
for(ClassDecl cd : prog.classDeclList) {
int cdPatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMP, Reg.CB, 0);
Machine.patch(cdPatch, cd.visit(this, 0));
cd.visit(this, cdPatch);
}
// Build main function
mdLBOffset = Machine.linkDataSize;
methodDataOffset = Machine.linkDataSize;
int mainPatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMP, Reg.CB, 0);
Analyzer.mainMethod.visit(this, 0);
Analyzer.main.visit(this, null);
// Run main function
int mainLabel = Machine.nextInstrAddr();
RuntimeEntity main = Analyzer.mainMethod.entity;
RuntimeEntity main = Analyzer.main.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;
// Patch up
for(Integer instr : patches.keySet()) {
Code c = patches.get(instr);
c.modify(instr);
}
return null;
}
@ -99,138 +84,103 @@ public class Encoder implements Visitor<Integer, Integer> {
// /////////////////////////////////////////////////////////////////////////////
@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) {
public Object visitClassDecl(ClassDecl cd, Integer patch) {
// Get size of class
int size = 0;
for(FieldDecl fd : cd.fieldDeclList) {
if(!fd.isStatic) {
fd.visit(this, size);
size += getSize(fd.type);
size += fd.entity.size;
}
}
// 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);
if(md != Analyzer.main) {
md.visit(this, null);
}
}
// Build class descriptor
int label = Machine.nextInstrAddr();
Machine.patch(cd.entity.instr, label);
// Build Class Descriptor
int addr = Machine.nextInstrAddr();
Machine.patch(patch, addr);
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
Machine.emit(Op.LOADL, size); // Size of class
Machine.emit(Op.LOADA, Reg.CB, addr); // Address of class
return label;
// Save entity
cd.entity = new RuntimeEntity(size, addr, Reg.CB);
return null;
}
@Override
public Integer visitFieldDecl(FieldDecl fd, Integer addr) {
public Object visitFieldDecl(FieldDecl fd, Integer offset) {
// Only non-static fields should ever reach this method
int size = getSize(fd.type);
fd.entity = new RuntimeEntity(size, offset, Reg.OB);
// 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 null;
}
return 0;
}
@Override
public Integer visitMethodDecl(MethodDecl md, Integer init) {
public Object visitMethodDecl(MethodDecl md, Integer arg) {
mdLBOffset = Machine.linkDataSize;
// Reset for next local LB
methodDataOffset = Machine.linkDataSize;
if(init != 0) {
// Save Entity
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);
}
md.entity = new RuntimeEntity(size, addr, Reg.CB);
else {
// Setup parameters
int paramS = 0;
int parameterSize = 0;
for(ParameterDecl pd : md.parameterDeclList) {
paramS += getSize(md.type);
pd.visit(this, -paramS);
parameterSize += getSize(pd.type);
pd.visit(this, -parameterSize);
}
// Setup body
int addr = Machine.nextInstrAddr();
Machine.patch(md.entity.instr, addr);
// Build Body
for(Statement s : md.statementList) {
s.visit(this, 0);
s.visit(this, null);
}
// Setup return statement
// Setup Return
if(md.returnExp != null) {
md.returnExp.visit(this, 0);
md.returnExp.visit(this, null);
}
// Return from function
// Machine.emit(Op.HALT, 4, 0, 0); (See snapshot)
Machine.emit(Op.RETURN, md.entity.size, 0, paramS);
Machine.emit(Op.RETURN, size, 0, parameterSize);
return null;
}
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) {
public Object visitParameterDecl(ParameterDecl pd, Integer offset) {
// Builds runtime entity, offset at LB
// Builds runtime entity (should be negative of LB)
int size = getSize(pd.type);
pd.entity = new RuntimeEntity(size, addr, Reg.LB);
pd.entity = new RuntimeEntity(size, offset, Reg.LB);
return 0;
return null;
}
/**
* Variable declarations are never disposed (even in static closures).
*/
@Override
public Integer visitVarDecl(VarDecl decl, Integer addr) {
public Object visitVarDecl(VarDecl decl, Integer arg) {
// Builds runtime entity, offset at LB
int size = getSize(decl.type);
decl.entity = new RuntimeEntity(size, addr, Reg.LB);
decl.entity = new RuntimeEntity(size, methodDataOffset, Reg.LB);
// Allocates space on stack
Machine.emit(Op.PUSH, size);
methodDataOffset += size;
return 0;
return null;
}
@ -241,21 +191,21 @@ public class Encoder implements Visitor<Integer, Integer> {
// /////////////////////////////////////////////////////////////////////////////
@Override
public Integer visitBaseType(BaseType type, Integer arg) {
public Object visitBaseType(BaseType type, Integer arg) {
return 0;
return null;
}
@Override
public Integer visitClassType(ClassType type, Integer arg) {
public Object visitClassType(ClassType type, Integer arg) {
return 0;
return null;
}
@Override
public Integer visitArrayType(ArrayType type, Integer arg) {
public Object visitArrayType(ArrayType type, Integer arg) {
return 0;
return null;
}
@ -265,171 +215,187 @@ public class Encoder implements Visitor<Integer, Integer> {
//
// /////////////////////////////////////////////////////////////////////////////
/**
* 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) {
public Object 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);
s.visit(this, null);
if(s instanceof VarDeclStmt) {
VarDeclStmt vds = (VarDeclStmt) s;
size += vds.varDecl.entity.size;
}
}
// Pop off variable declarations
if(size > 0) {
Machine.emit(Op.POP, size);
return 0;
methodDataOffset -= size;
}
/**
* 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);
return null;
}
@Override
public Object visitVardeclStmt(VarDeclStmt stmt, Integer arg) {
stmt.varDecl.visit(this, null);
stmt.initExp.visit(this, null);
// Assign value
RuntimeEntity e = stmt.varDecl.entity;
Machine.emit(Op.STORE, e.size, e.register, e.addr);
Machine.emit(Op.STORE, e.size, e.reg, e.addr);
// Update position
mdLBOffset += getSize(stmt.varDecl.type);
return 0;
return null;
}
@Override
public Integer visitAssignStmt(AssignStmt stmt, Integer arg) {
public Object visitAssignStmt(AssignStmt stmt, Integer arg) {
// Can potentially reach declaration directly
if(stmt.ref instanceof QualifiedRef) {
QualifiedRef ref = (QualifiedRef) stmt.ref;
MemberDecl md = (MemberDecl) ref.id.decl;
// Just access directly
if(md.isStatic) {
stmt.val.visit(this, null);
patches.put(Machine.nextInstrAddr(), new Code(md, true));
Machine.emit(Op.STORE, getSize(md.type), Reg.SB, 0);
return null;
}
// Access member directly
else if(ref.ref instanceof ThisRef) {
stmt.val.visit(this, null);
int addr = Machine.nextInstrAddr();
int size = getSize(ref.id.decl.type);
patches.put(addr, new Code(ref.id.decl, true));
Machine.emit(Op.STORE, size, Reg.OB, 0);
return null;
}
}
// Must access member iteratively
stmt.ref.visit(this, 1);
stmt.val.visit(this, null);
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);
int addr = Machine.nextInstrAddr();
int size = getSize(stmt.ref.decl.type);
patches.put(addr, new Code(stmt.ref.decl, true));
Machine.emit(Op.STORE, size, Reg.ZR, 0);
}
return 0;
return null;
}
/**
* 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) {
public Object 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);
stmt.argList.get(0).visit(this, null);
Machine.emit(Prim.putintnl);
return 0;
return null;
}
// 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);
stmt.argList.get(i).visit(this, null);
}
// Call Method
stmt.methodRef.visit(this, 0);
// Call method directly
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);
patches.put(Machine.nextInstrAddr(), new Code(md, true));
Machine.emit(Op.CALL, Reg.CB, 0);
}
return 0;
// Get address of qualified object
else {
if(stmt.methodRef instanceof QualifiedRef) {
QualifiedRef ref = (QualifiedRef) stmt.methodRef;
ref.ref.visit(this, null);
} else {
Machine.emit(Op.LOADA, Machine.addressSize, Reg.OB, 0);
}
patches.put(Machine.nextInstrAddr(), new Code(md, true));
Machine.emit(Op.CALLI, Reg.CB, 0);
}
// Clear off stack if necessary
int returnSize = getSize(md.type);
if(returnSize > 0) {
Machine.emit(Op.POP, returnSize);
}
return null;
}
@Override
public Integer visitIfStmt(IfStmt stmt, Integer arg) {
public Object visitIfStmt(IfStmt stmt, Integer arg) {
stmt.cond.visit(this, 0);
stmt.cond.visit(this, null);
// Build Then Statement
// Do not have to build as many jump instructions in this case
if(stmt.elseStmt == null) {
int ifPatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMPIF, Machine.trueRep, Reg.CB, 0);
Machine.emit(Op.JUMPIF, Machine.falseRep, Reg.CB, 0);
int elsePatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMP, Reg.CB, 0);
stmt.thenStmt.visit(this, null);
Machine.patch(ifPatch, Machine.nextInstrAddr());
int thenLabel = Machine.nextInstrAddr();
stmt.thenStmt.visit(this, 0);
return null;
}
// Must jump out at end of 'if' clause
int ifPatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMPIF, Machine.falseRep, Reg.CB, 0);
stmt.thenStmt.visit(this, null);
int thenPatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMP, Reg.CB, 0);
// Connect labels/patches
int endLabel = Machine.nextInstrAddr();
Machine.patch(elsePatch, endLabel);
// Build 'else' clause
Machine.patch(ifPatch, Machine.nextInstrAddr());
stmt.elseStmt.visit(this, null);
Machine.patch(thenPatch, Machine.nextInstrAddr());
if(stmt.elseStmt != null) {
stmt.elseStmt.visit(this, 0);
endLabel = Machine.nextInstrAddr();
return null;
}
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) {
public Object visitWhileStmt(WhileStmt stmt, Integer arg) {
// Must check the condition each loop
int whileLabel = Machine.nextInstrAddr();
stmt.cond.visit(this, 0);
stmt.cond.visit(this, null);
// Jump out once condition fails
int whileEndPatch = Machine.nextInstrAddr();
Machine.emit(Op.JUMPIF, Machine.falseRep, Reg.CB, 0);
// Execute
stmt.body.visit(this, 1);
stmt.body.visit(this, null);
Machine.emit(Op.JUMP, Reg.CB, whileLabel);
Machine.patch(whileEndPatch, Machine.nextInstrAddr());
return 0;
return null;
}
@ -440,9 +406,9 @@ public class Encoder implements Visitor<Integer, Integer> {
// /////////////////////////////////////////////////////////////////////////////
@Override
public Integer visitUnaryExpr(UnaryExpr expr, Integer arg) {
public Object visitUnaryExpr(UnaryExpr expr, Integer arg) {
expr.expr.visit(this, 0);
expr.expr.visit(this, null);
switch(expr.operator.spelling) {
case "!":
Machine.emit(Prim.not);
@ -452,115 +418,105 @@ public class Encoder implements Visitor<Integer, Integer> {
break;
}
return 0;
return null;
}
@Override
public Integer visitBinaryExpr(BinaryExpr expr, Integer arg) {
expr.left.visit(this, 0);
expr.right.visit(this, 0);
expr.operator.visit(this, 0);
public Object visitBinaryExpr(BinaryExpr expr, Integer arg) {
return 0;
}
String op = expr.operator.spelling;
@Override
public Integer visitRefExpr(RefExpr expr, Integer arg) {
if(op.equals("&&") || op.equals("||")) {
int rep = (op.equals("&&")) ? Machine.falseRep : Machine.trueRep;
expr.ref.visit(this, 0);
expr.left.visit(this, null);
int leftJump = Machine.nextInstrAddr();
Machine.emit(Op.JUMPIF, rep, Reg.CB, 0);
// Build code accordingly
if(expr.ref instanceof QualifiedRef) {
expr.right.visit(this, null);
expr.operator.visit(this, null);
// 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);
Machine.patch(leftJump, Machine.nextInstrAddr());
}
else {
expr.ref.entity.load();
expr.left.visit(this, null);
expr.right.visit(this, null);
expr.operator.visit(this, null);
}
return 0;
return null;
}
@Override
public Integer visitCallExpr(CallExpr expr, Integer arg) {
public Object visitRefExpr(RefExpr expr, Integer arg) {
expr.ref.visit(this, null);
return null;
}
@Override
public Object visitCallExpr(CallExpr expr, Integer arg) {
MethodDecl md = (MethodDecl) expr.functionRef.decl;
// 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);
expr.argList.get(i).visit(this, null);
}
// Call method
MethodDecl md = (MethodDecl) expr.functionRef.decl;
expr.functionRef.visit(this, 0);
// Call Method
if(md.isStatic) {
Machine.emit(Op.CALL, Reg.CB, md.entity.addr);
patches.put(Machine.nextInstrAddr(), new Code(md, true));
Machine.emit(Op.CALL, Reg.CB, 0);
} else {
expr.functionRef.entity.call();
Machine.emit(Op.CALLI, Reg.CB, md.entity.addr);
if(expr.functionRef instanceof QualifiedRef) {
QualifiedRef ref = (QualifiedRef) expr.functionRef;
ref.ref.visit(this, null);
} else {
Machine.emit(Op.LOADA, Machine.addressSize, Reg.OB, 0);
}
return 0;
patches.put(Machine.nextInstrAddr(), new Code(md, true));
Machine.emit(Op.CALLI, Reg.CB, 0);
}
return null;
}
@Override
public Integer visitLiteralExpr(LiteralExpr expr, Integer arg) {
public Object visitLiteralExpr(LiteralExpr expr, Integer arg) {
expr.literal.visit(this, 0);
expr.literal.visit(this, null);
return 0;
return null;
}
@Override
public Integer visitNewObjectExpr(NewObjectExpr expr, Integer arg) {
public Object visitNewObjectExpr(NewObjectExpr expr, Integer arg) {
Declaration decl = expr.classtype.className.decl;
patches.put(Machine.nextInstrAddr(), new Code(decl, true));
Machine.emit(Op.LOADA, Reg.CB, 0);
patches.put(Machine.nextInstrAddr(), new Code(decl, false));
Machine.emit(Op.LOADL, 0);
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;
return null;
}
/**
* 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) {
public Object 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);
expr.sizeExpr.visit(this, null);
Machine.emit(Prim.newarr);
return addr;
return null;
}
@ -571,49 +527,91 @@ public class Encoder implements Visitor<Integer, Integer> {
// /////////////////////////////////////////////////////////////////////////////
@Override
public Integer visitQualifiedRef(QualifiedRef ref, Integer arg) {
public Object visitQualifiedRef(QualifiedRef ref, Integer arg) {
ref.ref.visit(this, 0);
// Must be accessing length of an array
// Array type always returns value
if(ref.ref.decl.type.typeKind == TypeKind.ARRAY) {
ref.decl = ref.ref.decl;
ref.entity = ref.ref.entity;
// Get address of object
if(ref.ref instanceof QualifiedRef) {
ref.ref.visit(this, null);
} else if(ref.ref instanceof ThisRef) {
patches.put(Machine.nextInstrAddr(), new Code(ref.id.decl, true));
Machine.emit(Op.STORE, getSize(ref.id.decl.type), Reg.OB, 0);
} else {
patches.put(Machine.nextInstrAddr(), new Code(ref.ref.decl, true));
Machine.emit(Op.LOAD, Machine.addressSize, Reg.LB, 0);
}
// Access class member
else {
ref.entity = ref.id.decl.entity;
ref.entity.parent = ref.ref.entity;
Machine.emit(Op.LOADL, 1);
Machine.emit(Prim.sub);
Machine.emit(Op.LOADI, Machine.integerSize);
return null;
}
return 0;
MemberDecl md = (MemberDecl) ref.id.decl;
// Assigning
if(arg != null) {
ref.ref.visit(this, null);
patches.put(Machine.nextInstrAddr(), new Code(ref.id.decl, true));
Machine.emit(Op.LOADL, 0);
}
// Retrieving
else if(md.isStatic) {
patches.put(Machine.nextInstrAddr(), new Code(md, true));
Machine.emit(Op.LOAD, getSize(md.type), Reg.SB, 0);
} else {
ref.ref.visit(this, null);
int addr = Machine.nextInstrAddr();
patches.put(addr, new Code(ref.decl, true));
Machine.emit(Op.LOADL, 0);
Machine.emit(Prim.fieldref);
}
return null;
}
@Override
public Integer visitIndexedRef(IndexedRef ref, Integer arg) {
public Object visitIndexedRef(IndexedRef ref, Integer arg) {
ref.ref.visit(this, null);
ref.indexExpr.visit(this, null);
// Retrieving
if(arg == null) {
Machine.emit(Prim.arrayref);
}
return null;
}
@Override
public Object visitIdRef(IdRef ref, Integer arg) {
ref.entity = ref.decl.entity;
return 0;
// Retrieving
if(arg == null) {
int size = getSize(ref.decl.type);
int addr = Machine.nextInstrAddr();
patches.put(addr, new Code(ref.decl, true));
Machine.emit(Op.LOAD, size, Reg.ZR, 0);
}
return null;
}
@Override
public Integer visitIdRef(IdRef ref, Integer arg) {
public Object visitThisRef(ThisRef ref, Integer arg) {
ref.entity = ref.decl.entity;
Machine.emit(Op.LOADA, Machine.addressSize, Reg.OB, 0);
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;
return null;
}
@ -624,15 +622,15 @@ public class Encoder implements Visitor<Integer, Integer> {
// /////////////////////////////////////////////////////////////////////////////
@Override
public Integer visitIdentifier(Identifier id, Integer arg) {
public Object visitIdentifier(Identifier id, Integer arg) {
return 0;
return null;
}
@Override
public Integer visitOperator(Operator op, Integer arg) {
public Object visitOperator(Operator op, Integer arg) {
switch(op.token.spelling) {
switch(op.spelling) {
case "+":
Machine.emit(Prim.add);
break;
@ -671,20 +669,20 @@ public class Encoder implements Visitor<Integer, Integer> {
break;
}
return 0;
return null;
}
@Override
public Integer visitIntLiteral(IntLiteral num, Integer arg) {
public Object visitIntLiteral(IntLiteral num, Integer arg) {
Integer lit = Integer.parseInt(num.spelling);
Machine.emit(Op.LOADL, lit.intValue());
return 0;
return null;
}
@Override
public Integer visitBooleanLiteral(BooleanLiteral bool, Integer arg) {
public Object visitBooleanLiteral(BooleanLiteral bool, Integer arg) {
if(bool.spelling.equals("true")) {
Machine.emit(Op.LOADL, Machine.trueRep);
@ -692,7 +690,35 @@ public class Encoder implements Visitor<Integer, Integer> {
Machine.emit(Op.LOADL, Machine.falseRep);
}
return null;
}
// /////////////////////////////////////////////////////////////////////////////
//
// Convenience Methods
//
// /////////////////////////////////////////////////////////////////////////////
/**
* 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;
}
}
}

View File

@ -1,52 +1,19 @@
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
public Reg reg;
public int size;
public int addr;
// For use with nested elements
public boolean indirect = false;
public RuntimeEntity parent = null;
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) {
public RuntimeEntity(int size, int addr, Reg reg) {
this.reg = reg;
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);
}
}
}

View File

@ -1,13 +1,21 @@
package miniJava;
import java.io.*;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.FileNotFoundException;
import mJAM.*;
import miniJava.SyntacticAnalyzer.*;
import mJAM.Disassembler;
import mJAM.Interpreter;
import mJAM.ObjectFile;
import miniJava.SyntacticAnalyzer.Scanner;
import miniJava.SyntacticAnalyzer.Parser;
import miniJava.AbstractSyntaxTrees.Package;
import miniJava.ContextualAnalyzer.Analyzer;
import miniJava.AbstractSyntaxTrees.ASTDisplay;
import miniJava.CodeGenerator.Encoder;
import miniJava.Exceptions.*;
import miniJava.ContextualAnalyzer.IdTable;
import miniJava.ContextualAnalyzer.Analyzer;
import miniJava.ContextualAnalyzer.Reporter;
public class Compiler {
@ -27,14 +35,21 @@ public class Compiler {
Parser parser = new Parser(scanner);
Package p = parser.parse();
// Identification/Type Checking
// Display
ASTDisplay display = new ASTDisplay();
display.showTree(p);
// Contextual Analyzer
IdTable table = new IdTable();
Analyzer analyzer = new Analyzer();
analyzer.visitPackage(p, null);
int analyzed = analyzer.validate();
analyzer.visitPackage(p, table);
// Begin Compilation to mJAM
if(analyzed == 0) {
// Compilation
if(Reporter.error) {
System.exit(rc);
} else {
// Build mJAM assembly
Encoder encoder = new Encoder();
encoder.visitPackage(p, null);
@ -43,20 +58,33 @@ public class Compiler {
String objectFileName = args[0].substring(0, pos) + ".mJAM";
ObjectFile objF = new ObjectFile(objectFileName);
if(objF.write()) {
System.out.println("***Object File Failed.");
}
Reporter.emit("Object File Failed.");
}
System.exit(analyzed);
// create asm file using disassembler
String asmCodeFileName = "test.asm";
System.out.print("Writing assembly file ... ");
Disassembler d = new Disassembler(objectFileName);
if (d.disassemble()) {
System.out.println("FAILED!");
return;
}
else
System.out.println("SUCCEEDED");
// run
System.out.println("Running code ... ");
Interpreter.debug(objectFileName, asmCodeFileName);
System.out.println("*** mJAM execution completed");
}
System.exit(0);
} catch (FileNotFoundException e) {
System.out.println("***" + e.getMessage());
Reporter.emit(e.getMessage());
} catch (IOException e) {
System.out.println("***" + e.getMessage());
} catch (ScanningException e) {
System.out.println("***" + e.getMessage());
} catch (ParsingException e) {
System.out.println("***" + e.getMessage());
Reporter.emit(e.getMessage());
}
System.exit(rc);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,127 @@
package miniJava.ContextualAnalyzer;
import java.util.HashMap;
import java.util.ArrayList;
import miniJava.Compiler;
import miniJava.AbstractSyntaxTrees.*;
/**
*
*/
public class IdTable {
private IdTable parent;
private ArrayList<HashMap<String, Declaration>> scope;
// /////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTORS
//
// /////////////////////////////////////////////////////////////////////////////
/**
*
*/
public IdTable() {
this(null);
}
/**
*
* @param parent
*/
public IdTable(IdTable parent) {
this.parent = parent;
this.scope = new ArrayList<>();
push();
}
// /////////////////////////////////////////////////////////////////////////////
//
// ACTIVE SCOPE
//
// /////////////////////////////////////////////////////////////////////////////
/**
*
*/
public void pop() {
int last = scope.size() - 1;
scope.remove(last);
}
/**
*
*/
public void push() {
HashMap<String, Declaration> nested = new HashMap<>();
scope.add(nested);
}
/**
*
*/
public void add(Declaration decl) {
for(int i = 0; i < scope.size(); i++) {
HashMap<String, Declaration> nest = scope.get(i);
if(nest.containsKey(decl.name)) {
Declaration prev = nest.get(decl.name);
if(decl instanceof ClassDecl) {
Reporter.report(decl, prev, "Class");
} else if(decl instanceof FieldDecl) {
Reporter.report(decl, prev, "Field");
} else if(decl instanceof MethodDecl) {
Reporter.report(decl, prev, "Method");
} else if(decl instanceof ParameterDecl) {
Reporter.report(decl, prev, "Parameter");
} else if(decl instanceof VarDecl) {
Reporter.report(decl, prev, "Variable");
}
System.exit(Compiler.rc);
}
}
scope.get(scope.size()-1).put(decl.name, decl);
}
// /////////////////////////////////////////////////////////////////////////////
//
// GETTERS
//
// /////////////////////////////////////////////////////////////////////////////
/**
*
* @param name
*/
public Declaration getDeclaration(String name) {
IdTable current = this;
while (current != null) {
Declaration decl = current.getDeclarationAtScope(name);
if (decl == null) current = current.parent;
else return decl;
}
return null;
}
/**
*
* @param name
*/
public Declaration getDeclarationAtScope(String name) {
for (int i = scope.size() - 1; i >= 0; i--) {
HashMap<String, Declaration> nest = scope.get(i);
if (nest.containsKey(name)) return nest.get(name);
}
return null;
}
}

View File

@ -1,221 +0,0 @@
package miniJava.ContextualAnalyzer;
import java.util.*;
import miniJava.AbstractSyntaxTrees.*;
public class IdentificationTable {
private IdentificationTable parent;
private HashMap<String, IdentificationTable> scope;
private ArrayList<HashMap<String, Declaration>> table;
/**
*
*/
public IdentificationTable() {
this(null);
}
/**
*
* @param parent
*/
public IdentificationTable(IdentificationTable parent) {
this.parent = parent;
this.scope = new HashMap<String, IdentificationTable>();
this.table = new ArrayList<HashMap<String, Declaration>>();
this.table.add(new HashMap<String, Declaration>());
}
/**
* Adds another level for variables to be stored at
*/
public void popLevel() {
table.remove(table.size() - 1);
}
/**
* Removes all variables declared at the current level
*/
public void pushLevel() {
table.add(new HashMap<String, Declaration>());
}
/**
* This method will only ever be called with class/method declarations.
*
* @param decl
* @return
*/
public IdentificationTable openScope(Declaration decl) {
Declaration current = getDeclarationAtScope(decl.name);
if (scope.containsKey(decl.name) || current != null) {
Reporter.report(ErrorType.REDEFINITION, decl, current);
return null;
} else {
table.get(table.size() - 1).put(decl.name, decl);
scope.put(decl.name, new IdentificationTable(this));
return scope.get(decl.name);
}
}
/**
* Return nested scope corresponding to declaration (or null if
* non-existant).
*
* @param decl
* @return
*/
public IdentificationTable getScope(Declaration decl) {
if (scope.containsKey(decl.name)) {
return scope.get(decl.name);
}
return null;
}
/**
* Iterates through all parents and tries to find the specified declaration
* by name.
*
* @param name
* @return
*/
public Declaration getDeclaration(String name) {
IdentificationTable current = this;
while (current != null) {
Declaration decl = current.getDeclarationAtScope(name);
if (decl == null) current = current.parent;
else return decl;
}
return null;
}
/**
* Iterates through levels (from higher to lower) for declaration, returning
* none if it does not exist.
*
* @param name
* @return
*/
public Declaration getDeclarationAtScope(String name) {
for (int i = table.size() - 1; i >= 0; i--) {
HashMap<String, Declaration> level = table.get(i);
if (level.containsKey(name)) return level.get(name);
}
return null;
}
/**
* Add declaration to current table's table member.
*
* @param name
*/
public void setDeclarationAtScope(Declaration decl) {
for (int i = 0; i < table.size(); i++) {
HashMap<String, Declaration> level = table.get(i);
if (level.containsKey(decl.name)) {
Declaration defined = level.get(decl.name);
Reporter.report(ErrorType.REDEFINITION, decl, defined);
return;
}
}
table.get(table.size() - 1).put(decl.name, decl);
}
/**
* Checks whether the specified class has been declared.
*
* @param t
* @return
*/
public boolean classExists(Type t) {
if (t.typeKind == TypeKind.CLASS) {
ClassType ct = (ClassType) t;
return getDeclaration(ct.className.spelling) != null;
}
return true;
}
/**
* Determines whether two types match.
*
* @param t1
* @param t2
* @return
*/
public static boolean match(Type t1, Type t2) {
return IdentificationTable.match(t1, t2, false);
}
/**
* Determines whether two type match, reporting an error if they do not.
*
* @param t1
* @param t2
* @param report
* @return
*/
public static boolean match(Type t1, Type t2, boolean report) {
if (t1.typeKind != t2.typeKind) {
if (report) Reporter.report(ErrorType.TYPE_MISMATCH, t1, t2);
return false;
}
// Check Class Types match
else if (t1.typeKind == TypeKind.CLASS) {
ClassType c1 = (ClassType) t1;
ClassType c2 = (ClassType) t2;
if (!c1.className.spelling.equals(c2.className.spelling)) {
if (report) Reporter.report(ErrorType.TYPE_MISMATCH, t1, t2);
return false;
}
}
// Check array types match
else if (t1.typeKind == TypeKind.ARRAY) {
ArrayType a1 = (ArrayType) t1;
ArrayType a2 = (ArrayType) t2;
if (!IdentificationTable.match(a1.eltType, a2.eltType)) {
if (report) Reporter.report(ErrorType.TYPE_MISMATCH, t1, t2);
return false;
}
}
return true;
}
/**
* Determines if the passed method is a valid entry point for the
* compilation phase.
*
* @param md
* @return
*/
public static boolean isMainMethod(MethodDecl md) {
// Check Declaration
if (!md.isPrivate && md.isStatic && md.type.typeKind == TypeKind.VOID
&& md.name.equals("main") && md.parameterDeclList.size() == 1) {
// Check Parameter Declaration
ParameterDecl pd = md.parameterDeclList.get(0);
if (pd.type.typeKind != TypeKind.ARRAY) return false;
ArrayType at = (ArrayType) pd.type;
if (at.eltType.typeKind != TypeKind.CLASS) return false;
ClassType ct = (ClassType) at.eltType;
return ct.className.spelling.equals("String");
}
return false;
}
}

View File

@ -1,205 +1,27 @@
package miniJava.ContextualAnalyzer;
import miniJava.AbstractSyntaxTrees.*;
enum ErrorType {
THIS,
LENGTH,
VOID_TYPE,
CLASS_IDENTIFER,
VARDECL_USED,
NONFUNCTION_CALL,
FUNCTION_ASSIGNMENT,
UNDEFINED,
STATIC,
VISIBILITY,
NO_RETURN,
TYPE_MISMATCH,
REDEFINITION,
MAIN_UNDECLARED,
INVALID_PARAM_COUNT,
MULTIPLE_MAIN,
UNDECLARED_TYPE,
SINGLE_VARCOND,
INVALID_INDEX
}
import miniJava.AbstractSyntaxTrees.Declaration;
public class Reporter {
public static boolean error = false;
/**
* Convenience function for getting type names.
*
* @param t
* @return
*/
private static String getTypeName(Type t) {
if (t instanceof ClassType) {
ClassType ct = (ClassType) t;
return ct.className.spelling;
} else if (t instanceof ArrayType) {
ArrayType at = (ArrayType) t;
return getTypeName(at.eltType);
}
return t.typeKind.toString();
}
/**
* Convenience method for formatting error message.
*
* @param message
*/
private static void emit(String message) {
public static void emit(String message) {
error = true;
System.out.println("***" + message);
}
/**
* Convenience function for managing all error types.
*
* @param type
* @param a1
* @param a2
* Redefinitions
* @param d1
* @param d2
*/
public static void report(ErrorType type, AST a1, AST a2) {
switch (type) {
// Cannot access 'this' in a static method
case THIS: {
MethodDecl md = (MethodDecl) a2;
emit("Cannot reference 'this' " + a1.posn + " in static method '" + md.name + "' " + md.posn);
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 outside of a qualified reference at " + a1.posn);
break;
}
// Cannot have a parameter of type void
case VOID_TYPE: {
emit("Cannot have a parameter of type void at " + a1.posn);
break;
}
// Attempting to call a non function as a function
case NONFUNCTION_CALL: {
emit("Not a valid function call at " + a1.posn);
break;
}
// Cannot assign a value to a function
case FUNCTION_ASSIGNMENT: {
emit("Cannot assign a value to a function at " + a1.posn);
break;
}
// Tried accessing a non-static member from a static method
case STATIC: {
MemberDecl md = (MemberDecl) a1;
Identifier ident = (Identifier) a2;
emit("'" + md.name + "' " + md.posn + " is an instance member and cannot be accessed at " + ident.posn);
break;
}
// Tried accessing a private member of a different class
case VISIBILITY: {
MemberDecl md = (MemberDecl) a1;
Identifier ident = (Identifier) a2;
emit("'" + md.name + "' " + md.posn + " is a private member and cannot be accessed at " + ident.posn);
break;
}
// Non-void function does not have a return statement
case NO_RETURN: {
MethodDecl md = (MethodDecl) a1;
emit("'" + md.name + "' " + md.posn + " must have a return statement");
break;
}
// The passed types are not the same
case TYPE_MISMATCH: {
String name1 = getTypeName((Type) a1);
String name2 = getTypeName((Type) a2);
if(a1 instanceof ArrayType) name1 += " Array";
if(a2 instanceof ArrayType) name2 += " Array";
emit("Expected type '" + name1 + "' but got '" + name2 + "' " + a2.posn);
break;
}
// Attempting to redeclare a variable
case REDEFINITION: {
emit("Variable at " + a1.posn + " already declared earlier at " + a2.posn);
break;
}
// Identifier could not be found
case UNDEFINED: {
Identifier ident = (Identifier) a1;
emit("Identifier '" + ident.spelling + "' " + ident.posn + " is undeclared.");
break;
}
// A public static void main(String[] args) method was not declared
case MAIN_UNDECLARED: {
emit("A main function was not declared");
break;
}
// Parameter counts of an expression/statement do not match declaration
case INVALID_PARAM_COUNT: {
MethodDecl md = (MethodDecl) a2;
emit("Call to '" + md.name + "' " + a2.posn + " has an invalid parameter count at " + a1.posn);
break;
}
// A public static void main(String[] args) was declared more than once
case MULTIPLE_MAIN: {
emit("Main function at " + a1.posn + " already declared previously at " + a2.posn);
break;
}
// A reference has been made to a non-existant type
case UNDECLARED_TYPE: {
if(a1 instanceof Type) {
String typeName = getTypeName((Type) a1);
emit("'" + typeName + "' " + a1.posn + " has not been declared previously");
} else {
emit("Identifier at " + a1.posn + " could not be identified");
}
break;
}
// A Variable Declaration Statement was made as the only statement of a condition
case SINGLE_VARCOND: {
emit("Conditional statment cannot be followed by a variable declaration statement exclusively " + a1.posn);
break;
}
// An indexed expression must be of an int type
case INVALID_INDEX: {
emit("Index expression is not of type int " + a1.posn);
break;
}
// A variable declaration identifier was used in a var decl statement
case VARDECL_USED: {
emit("Identifier at " + a1.posn + " cannot refer to the variable declaration at " + a2.posn);
break;
}
}
error = true;
public static void report(Declaration d1, Declaration d2, String prefix) {
emit(prefix + " at " + d1.posn + " previously defined at " + d2.posn);
}
}

View File

@ -1,10 +0,0 @@
package miniJava.Exceptions;
public class IdentificationException extends Exception {
private static final long serialVersionUID = 1L;
public IdentificationException() {
super("Identification Error!");
}
}

View File

@ -1,17 +0,0 @@
package miniJava.Exceptions;
import miniJava.SyntacticAnalyzer.Token;
public class ParsingException extends Exception {
private static final long serialVersionUID = 1L;
public ParsingException() {
super("Unidentified parsing error!");
}
public ParsingException(Token t) {
super("Parsing error with " + t.spelling + " at " + t.posn.toString());
}
}

View File

@ -1,11 +0,0 @@
package miniJava.Exceptions;
public class ScanningException extends Exception {
private static final long serialVersionUID = 1L;
public ScanningException(int col, int line) {
super("Scanning error at Column: " + col + ", Line: " + line);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
package miniJava.SyntacticAnalyzer;
import java.io.IOException;
/**
*
*/
public class ParsingException extends IOException {
private static final long serialVersionUID = 1L;
public ParsingException(SourcePosition posn) {
super("Parsing error at " + posn);
}
}

View File

@ -1,313 +1,306 @@
package miniJava.SyntacticAnalyzer;
import java.io.*;
import miniJava.Exceptions.*;
public class Scanner {
private int col = 1;
private int line = 1;
private boolean predefined;
private BufferedReader input;
/**
*
* @param input
*/
public Scanner(BufferedReader input) {
this.input = input;
}
public Scanner(String input) {
StringReader reader = new StringReader(input);
this.input = new BufferedReader(reader);
this(input, false);
}
/**
* Scans in input, returning next token.
*
* @param input
* @param predefined
*/
public Scanner(String input, boolean predefined) {
this(new BufferedReader(new StringReader(input)), predefined);
}
/**
*
* @param input
* @param predefined
*/
public Scanner(BufferedReader input, boolean predefined) {
this.input = input;
this.predefined = predefined;
}
// /////////////////////////////////////////////////////////////////////////////
//
// Scanning
//
// /////////////////////////////////////////////////////////////////////////////
/**
*
* @return
* @throws IOException
*/
public Token scan() throws ScanningException {
String attr = "";
public Token scan() throws IOException {
Token token = null;
String spelling = "";
while (token == null) {
// Check for EOF
int c = read();
if (c == -1)
return new Token("", Token.TYPE.EOT);
SourcePosition posn = new SourcePosition(col, line);
// Setup
attr += (char) c;
if(c == -1) {
token = new Token("", Token.TYPE.EOT, posn);
} else {
spelling += (char) c;
switch (c) {
switch(c) {
// Operators
case '*':
token = new Token(attr, Token.TYPE.BINOP);
break;
case '+':
if (peek('+'))
throw new ScanningException(col, line);
token = new Token(attr, Token.TYPE.BINOP);
case '-': {
if(peek(c)) throw new ScanningException(posn);
token = new Token(spelling, Token.TYPE.BINOP, posn);
break;
}
case '-':
if (peek('-'))
throw new ScanningException(col, line);
token = new Token(attr, Token.TYPE.BINOP);
break;
// Check for comment
case '/':
if (peek('*')) {
// Comment
case '/': {
if(peek('*')) {
read();
readComment();
attr = "";
} else if (peek('/')) {
readLine();
attr = "";
} else
token = new Token(attr, Token.TYPE.BINOP);
break;
readMultiLineComment();
spelling = "";
} else if(peek('/')) {
readSingleLineComment();
spelling = "";
} else {
token = new Token(spelling, Token.TYPE.BINOP, posn);
}
// Check for c or c=
break;
}
// Relational
case '>':
case '<':
if (peek('='))
attr += (char) read();
token = new Token(attr, Token.TYPE.BINOP);
case '<': {
if (peek('=')) spelling += (char) read();
token = new Token(spelling, Token.TYPE.BINOP, posn);
break;
// Check for ! or !=
case '!':
if (!peek('='))
token = new Token(attr, Token.TYPE.UNOP);
else {
attr += (char) read();
token = new Token(attr, Token.TYPE.BINOP);
}
break;
// Check for && or ||
// Negation
case '!': {
if(peek('=')) {
spelling += (char) read();
token = new Token(spelling, Token.TYPE.BINOP, posn);
} else {
token = new Token(spelling, Token.TYPE.UNOP, posn);
}
break;
}
// Logical
case '&':
case '|':
if (!peek((char) c))
throw new ScanningException(col, line);
else {
attr += (char) read();
token = new Token(attr, Token.TYPE.BINOP);
case '|': {
if(!peek(c)) {
throw new ScanningException(posn);
} else {
spelling += (char) read();
token = new Token(spelling, Token.TYPE.BINOP, posn);
}
break;
}
// Other Operators
case '=':
if (!peek('='))
token = new Token(attr, Token.TYPE.EQUALS);
else {
attr += (char) read();
token = new Token(attr, Token.TYPE.BINOP);
}
break;
case '.':
token = new Token(attr, Token.TYPE.PERIOD);
break;
case ',':
token = new Token(attr, Token.TYPE.COMMA);
break;
case '[':
token = new Token(attr, Token.TYPE.LSQUARE);
break;
case ']':
token = new Token(attr, Token.TYPE.RSQUARE);
break;
case '{':
token = new Token(attr, Token.TYPE.LBRACKET);
break;
case '}':
token = new Token(attr, Token.TYPE.RBRACKET);
break;
case '(':
token = new Token(attr, Token.TYPE.LPAREN);
break;
case ')':
token = new Token(attr, Token.TYPE.RPAREN);
break;
case ';':
token = new Token(attr, Token.TYPE.SEMICOLON);
break;
default:
// Identifier or Keyword
if (isAlpha((char) c)) {
for (char n = peek(); isAlpha(n) || isDigit(n);) {
attr += (char) read();
n = peek();
}
if (Token.keywords.containsKey(attr)) {
token = new Token(attr, Token.keywords.get(attr));
case '=': {
if(peek('=')) {
spelling += (char) read();
token = new Token(spelling, Token.TYPE.BINOP, posn);
} else {
token = new Token(attr, Token.TYPE.ID);
token = new Token(spelling, Token.TYPE.EQUALS, posn);
}
break;
}
// Miscellaneous
case '.':
case ',':
case '[':
case ']':
case '{':
case '}':
case '(':
case ')':
case ';': {
token = new Token(spelling, Token.symbols.get(c), posn);
break;
}
default: {
// Identifier or keyword
if(isAlpha(c)) {
int next = peek();
while(isAlpha(next) || isDigit(next) || next == '_') {
spelling += (char) read();
next = peek();
}
if(Token.keywords.containsKey(spelling)) {
token = new Token(spelling, Token.keywords.get(spelling), posn);
} else {
token = new Token(spelling, Token.TYPE.ID, posn);
}
}
// Number
else if (isDigit((char) c)) {
for (char n = peek(); isDigit(n);) {
attr += (char) read();
n = peek();
else if(isDigit(c)) {
int next = peek();
while(isDigit(next)) {
spelling += (char) read();
next = peek();
}
token = new Token(attr, Token.TYPE.NUM);
token = new Token(spelling, Token.TYPE.NUM, posn);
}
// Whitespace
else if (isWhitespace((char) c)) {
attr = "";
else if(isWhitespace(c)) {
spelling = "";
}
// Unrecognized Character
else
throw new ScanningException(col, line);
;
break;
else {
throw new ScanningException(posn);
}
}
}
}
}
token.posn = new SourcePosition(line, col - token.spelling.length());
return token;
}
// /////////////////////////////////////////////////////////////////////////////
//
// Convenience Methods
//
// /////////////////////////////////////////////////////////////////////////////
/**
*
* @param c
* @return
*/
private boolean isAlpha(int c) {
return (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (predefined && c == '_');
}
/**
*
* @param c
* @return
*/
private boolean isDigit(int c) {
return c >= '0' && c <= '9';
}
/**
*
* @param c
* @return
*/
private boolean isWhitespace(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}
/**
* Looks at next character in stream without consuming.
*
* @return
* @throws IOException
*/
private char peek() throws ScanningException {
try {
private int peek() throws IOException {
input.mark(1);
int next = input.read();
input.reset();
return next == -1 ? '\0' : (char) next;
} catch (IOException e) {
throw new ScanningException(col, line);
}
return next;
}
/**
* Returns whether passed character is next in stream.
*
* @param c
* @return
* @throws IOException
*/
private boolean peek(char c) throws ScanningException {
try {
private boolean peek(int c) throws IOException {
input.mark(1);
int next = input.read();
input.reset();
return c == next;
} catch (IOException e) {
throw new ScanningException(col, line);
}
}
/**
* Alternative reading that keeps track of position.
*
* @return
* @throws IOException
*/
private int read() throws ScanningException {
try {
private int read() throws IOException {
int next = input.read();
if (next != '\n' && next != '\r')
col += 1;
else {
if(next == '\n' || next == '\r') {
col = 1;
line += 1;
if (peek('\r') || peek('\n'))
next = input.read();
} else {
col += 1;
}
return next;
} catch (IOException e) {
throw new ScanningException(col, line);
}
}
/**
* Consumes input until an end of comment has been reached.
*
* @throws IOException
*/
private void readComment() throws ScanningException {
char prev = '\0', current = '\0';
while (prev != '*' || current != '/') {
private void readSingleLineComment() throws IOException {
col = 1;
line += 1;
input.readLine();
}
/**
*
* @throws IOException
*/
private void readMultiLineComment() throws IOException {
int prev = '\0';
int current = '\0';
while(prev != '*' || current != '/') {
prev = current;
current = read();
int next = read();
if (next == -1)
throw new ScanningException(col, line);
else
current = (char) next;
// Unterminated
if(current == -1) {
SourcePosition posn = new SourcePosition(line, col);
throw new ScanningException(posn);
}
}
/**
* Consumes input until the end of line is reached
*
* @throws IOException
*/
private void readLine() throws ScanningException {
for (int n = 0; n != '\n' && n != '\r' && n != -1; n = read()) {
}
}
/**
* Tells whether character is alphabetical.
*
* @param c
* @return
*/
private boolean isAlpha(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
/**
* Tells whether character is numerical.
*
* @param c
* @return
*/
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
/**
* Tells wheter character is whitespace.
*
* @param c
* @return
*/
private boolean isWhitespace(char c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}
}

View File

@ -0,0 +1,16 @@
package miniJava.SyntacticAnalyzer;
import java.io.IOException;
/**
*
*/
public class ScanningException extends IOException {
private static final long serialVersionUID = 1L;
public ScanningException(SourcePosition posn) {
super("Scanning error at " + posn);
}
}

View File

@ -1,11 +1,14 @@
package miniJava.SyntacticAnalyzer;
/**
*
*/
public class SourcePosition {
public final int col;
public final int line;
public SourcePosition(int line, int col) {
public SourcePosition(int col, int line) {
this.col = col;
this.line = line;
}

View File

@ -2,26 +2,51 @@ package miniJava.SyntacticAnalyzer;
import java.util.HashMap;
/**
*
*/
public class Token {
public enum TYPE {
// Possible Terminals
ID, NUM, UNOP, BINOP,
// Terminals
ID,
NUM,
UNOP,
BINOP,
EQUALS,
PERIOD,
COMMA,
LPAREN,
RPAREN,
LSQUARE,
RSQUARE,
LBRACKET,
RBRACKET,
SEMICOLON,
// Keywords
IF, ELSE, NEW, INT, VOID, THIS, TRUE, FALSE, CLASS, WHILE, RETURN, BOOLEAN,
// Declarators
STATIC, PUBLIC, PRIVATE,
// Other Terminals
EQUALS, PERIOD, COMMA, LPAREN, RPAREN, LSQUARE, RSQUARE, LBRACKET, RBRACKET, SEMICOLON,
IF,
ELSE,
NEW,
INT,
VOID,
THIS,
TRUE,
FALSE,
CLASS,
WHILE,
RETURN,
BOOLEAN,
STATIC,
PUBLIC,
PRIVATE,
// End of Token Stream
EOT
};
// Pair words with enumeration
public final static HashMap<String, TYPE> keywords;
static {
keywords = new HashMap<String, TYPE>();
@ -42,13 +67,28 @@ public class Token {
keywords.put("new", TYPE.NEW);
}
public final TYPE type;
public SourcePosition posn;
public final String spelling;
// Pair symbols with enumeration
public final static HashMap<Integer, TYPE> symbols;
static {
symbols = new HashMap<Integer, TYPE>();
symbols.put((int) '.', TYPE.PERIOD);
symbols.put((int) ',', TYPE.COMMA);
symbols.put((int) '[', TYPE.LSQUARE);
symbols.put((int) ']', TYPE.RSQUARE);
symbols.put((int) '{', TYPE.LBRACKET);
symbols.put((int) '}', TYPE.RBRACKET);
symbols.put((int) '(', TYPE.LPAREN);
symbols.put((int) ')', TYPE.RPAREN);
symbols.put((int) ';', TYPE.SEMICOLON);
}
public Token(String spelling, TYPE type) {
public final TYPE type;
public final String spelling;
public final SourcePosition posn;
public Token(String spelling, TYPE type, SourcePosition posn) {
this.type = type;
this.posn = null;
this.posn = posn;
this.spelling = spelling;
}
}

View File

@ -0,0 +1,71 @@
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");
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);
}
}
}
}

View File

@ -0,0 +1,98 @@
package tester;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.util.Scanner;
/* Automated regression tester for Checkpoint 2 tests
* Created by Max Beckman-Harned
* Put your tests in "tests/pa2_tests" folder in your Eclipse workspace directory
* If you preface your error messages / exceptions with ERROR or *** then they will be displayed if they appear during processing
*/
public class Checkpoint2 {
private static class ReturnInfo {
int returnCode;
String ast;
public ReturnInfo(int _returnCode, String _ast) {
returnCode = _returnCode;
ast = _ast;
}
}
public static void main(String[] args) throws IOException, InterruptedException {
File testDir = new File(System.getProperty("java.class.path")
+ "/../tests/pa2_tests");
int failures = 0;
for (File x : testDir.listFiles()) {
if (x.getName().endsWith("out") || x.getName().startsWith("."))
continue;
ReturnInfo info = runTest(x);
int returnCode = info.returnCode;
String ast = info.ast;
if (x.getName().indexOf("pass") != -1) {
if (returnCode == 0) {
String actualAST = getAST(new FileInputStream(x.getPath() + ".out"));
if (actualAST.equals(ast))
System.out.println(x.getName() + " parsed successfully and has a correct AST!");
else {
System.err.println(x.getName() + " parsed successfully but has an incorrect AST!");
failures++;
}
}
else {
failures++;
System.err.println(x.getName()
+ " failed to be parsed!");
}
} 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 ReturnInfo runTest(File x) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("java", "miniJava.Compiler", x.getPath()).directory(new File(System.getProperty("java.class.path")));
pb.redirectErrorStream(true);
Process p = pb.start();
String ast = getAST(p.getInputStream());
p.waitFor();
int exitValue = p.exitValue();
return new ReturnInfo(exitValue, ast);
}
public static String getAST(InputStream stream) {
Scanner scan = new Scanner(stream);
String ast = null;
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.equals("======= AST Display =========================")) {
line = scan.nextLine();
while(scan.hasNext() && !line.equals("=============================================")) {
ast += line + "\n";
line = scan.nextLine();
}
}
if (line.startsWith("*** "))
System.out.println(line);
if (line.startsWith("ERROR")) {
System.out.println(line);
while(scan.hasNext())
System.out.println(scan.next());
}
}
scan.close();
return ast;
}
}

View File

@ -0,0 +1,73 @@
package tester;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
/* Automated regression tester for Checkpoint 3 tests
* Created by Max Beckman-Harned
* Put your tests in "tests/pa3_tests" folder in your Eclipse workspace directory
* If you preface your error messages / exceptions with ERROR or *** then they will be displayed if they appear during processing
*/
public class Checkpoint3 {
public static void main(String[] args) throws IOException, InterruptedException {
File testDir = new File(System.getProperty("java.class.path")
+ "/../tests/pa3_tests");
int failures = 0;
for (File x : testDir.listFiles()) {
if (x.getName().endsWith("out") || x.getName().startsWith(".") || x.getName().endsWith("mJAM") || x.getName().endsWith("asm"))
continue;
int returnCode = runTest(x);
if (x.getName().indexOf("pass") != -1) {
if (returnCode == 0) {
System.out.println(x.getName() + " processed successfully!");
}
else {
failures++;
System.err.println(x.getName()
+ " failed to be processed!");
}
} 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")));
pb.redirectErrorStream(true);
Process p = pb.start();
processStream(p.getInputStream());
p.waitFor();
int exitValue = p.exitValue();
return exitValue;
}
public static void processStream(InputStream stream) {
Scanner scan = new Scanner(stream);
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.startsWith("*** "))
System.out.println(line);
if (line.startsWith("ERROR")) {
System.out.println(line);
//while(scan.hasNext())
//System.out.println(scan.next());
}
}
scan.close();
}
}

112
src/tester/Checkpoint4.java Normal file
View File

@ -0,0 +1,112 @@
package tester;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
/* Automated regression tester for Checkpoint 4 tests
* Created by Max Beckman-Harned
* Put your tests in "tests/pa4_tests" folder in your Eclipse workspace directory
* If you preface your compiler error messages / exceptions with ERROR or *** then they will be displayed if they appear during processing
*/
public class Checkpoint4 {
public static void main(String[] args) throws IOException, InterruptedException {
File testDir = new File(System.getProperty("java.class.path")
+ "/../tests/pa4_tests");
int failures = 0;
for (File x : testDir.listFiles()) {
if (x.getName().startsWith(".") || x.getName().endsWith("mJAM") || x.getName().endsWith("asm"))
continue;
int returnCode = runTest(x);
if (x.getName().indexOf("pass") != -1) {
if (returnCode == 0) {
try {
int val = executeTest(x);
int expected = Integer.parseInt(x.getName().substring(5,7));
if (val == expected)
System.out.println(x.getName() + " ran successfully!");
else {
failures++;
System.err.println(x.getName() + " compiled but did not run successfully--got output " + val);
}
}
catch(Exception ex) {
failures++;
System.err.println(x.getName() + " did not output correctly.");
}
}
else {
failures++;
System.err.println(x.getName()
+ " failed to be processed!");
}
} 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")));
pb.redirectErrorStream(true);
Process p = pb.start();
processStream(p.getInputStream());
p.waitFor();
int exitValue = p.exitValue();
return exitValue;
}
private static int executeTest(File x) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("java", "mJAM.Interpreter", x.getPath().replace(".java", ".mJAM")).directory(new File(System.getProperty("java.class.path")));
Process process = pb.start();
Scanner scan = new Scanner(process.getInputStream());
int num = -1;
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.startsWith(">>> ")) {
num = Integer.parseInt(line.substring(4));
System.out.println("Result = " + num);
break;
}
}
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.startsWith("*** ")) {
System.out.println(line);
break;
}
}
scan.close();
return num;
}
public static void processStream(InputStream stream) {
Scanner scan = new Scanner(stream);
while (scan.hasNextLine()) {
String line = scan.nextLine();
if (line.startsWith("*** "))
System.out.println(line);
if (line.startsWith("ERROR")) {
System.out.println(line);
//while(scan.hasNext())
//System.out.println(scan.next());
}
}
scan.close();
}
}