Working compiler.
parent
2f673d60b7
commit
8390199866
23
README.md
23
README.md
|
@ -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
|
||||
|
|
BIN
docs/PA1.pdf
BIN
docs/PA1.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -19,5 +19,9 @@ public class ArrayType extends Type {
|
|||
return v.visitArrayType(this, o);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return eltType + " Array";
|
||||
}
|
||||
|
||||
public Type eltType;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -17,5 +17,9 @@ public class ClassType extends Type {
|
|||
return v.visitClassType(this, o);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Class " + className.spelling;
|
||||
}
|
||||
|
||||
public Identifier className;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ public abstract class Reference extends AST {
|
|||
super(posn);
|
||||
}
|
||||
|
||||
public String spelling;
|
||||
public Declaration decl;
|
||||
public RuntimeEntity entity;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package miniJava.Exceptions;
|
||||
|
||||
public class IdentificationException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public IdentificationException() {
|
||||
super("Identification Error!");
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue