Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.git
*.class
*.ll
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* text=auto
*.sh text eol=lf
Makefile text eol=lf
*.jar binary
15 changes: 15 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: tests

on:
push:
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build compiler image
run: docker build -t minijava .
- name: Run test suite
run: docker run --rm minijava
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Build output
*.class

# Generated by JTB / JavaCC during `make compile`
syntaxtree/
visitor/
MiniJavaParser*.java
Token*.java
ParseException.java
JavaCharStream.java
113 changes: 72 additions & 41 deletions CheckingVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public String visit(ClassDeclaration n, Void argu) throws Exception {
String classname = n.f1.accept(this, null);
this.inClass = false;
Main.ST.enterClass(classname);
n.f3.accept(this, null);
n.f4.accept(this, null);
return null;
}
Expand All @@ -79,6 +80,7 @@ public String visit(ClassExtendsDeclaration n, Void argu) throws Exception {
String parentclassName = n.f3.accept(this, null);;
this.inClass = false;
Main.ST.enterClass(classname);
n.f5.accept(this, null);
n.f6.accept(this, null);
return null;
}
Expand Down Expand Up @@ -107,11 +109,13 @@ public String visit(MethodDeclaration n, Void argu) throws Exception {
String myName = n.f2.accept(this, null);
this.inClass = false;
Main.ST.enterMethod(myName);
String exprType = n.f10.accept(this, null);
n.f7.accept(this, null);
n.f8.accept(this, null);
if (!myType.equals(exprType)){
String exprType = n.f10.accept(this, null);
// the returned value must have the declared type or be a subclass of it
if (!myType.equals(exprType) && !Main.ST.childHasParent(exprType, myType)){
throw new Exception("MethodDeclaration: False return type"+"("+Main.ST.getCurrentMethodName()+")");
} // return statement bust be the same as the type
}
Main.ST.exitMethod();
return null;
}
Expand Down Expand Up @@ -220,7 +224,22 @@ public String visit(IntegerType n, Void argu) {
*/

public boolean isObject(String idType){
return idType != "int" && idType != "int[]" && idType != "boolean" && idType != "boolean[]";
return !idType.equals("int") && !idType.equals("int[]") && !idType.equals("boolean") && !idType.equals("boolean[]");
}

/**
* f0 -> "System.out.println"
* f1 -> "("
* f2 -> Expression()
* f3 -> ")"
* f4 -> ";"
*/
@Override
public String visit(PrintStatement n, Void argu) throws Exception {
if (!"int".equals(n.f2.accept(this, argu))){
throw new Exception("PrintStatement: only integers can be printed"+"("+Main.ST.getCurrentMethodName()+")");
}
return null;
}


Expand All @@ -240,7 +259,7 @@ public String visit(AssignmentStatement n, Void argu) throws Exception {
throw new Exception("AssignmentStatement: id is object but expr is not"+"("+Main.ST.getCurrentMethodName()+")");
}
}else{
if (idType != exprType){
if (!idType.equals(exprType)){
throw new Exception("AssignmentStatement: id and expr not the same type"+"("+Main.ST.getCurrentMethodName()+")");
}
}
Expand All @@ -260,9 +279,9 @@ public String visit(AssignmentStatement n, Void argu) throws Exception {
* f6 -> ";"
*/
private String arrayType(String type){
if (type =="boolean[]"){
if ("boolean[]".equals(type)){
return "boolean";
}else if (type == "int[]"){
}else if ("int[]".equals(type)){
return "int";
}
return null;
Expand All @@ -274,15 +293,15 @@ public String visit(ArrayAssignmentStatement n, Void argu) throws Exception {
String _ret=null;
String idType = n.f0.accept(this, argu);
n.f1.accept(this, argu);
if (n.f2.accept(this, argu) != "int"){
if (!"int".equals(n.f2.accept(this, argu))){
throw new Exception("ArrayAssignmentStatement: expr index is not an integer"+"("+Main.ST.getCurrentMethodName()+")");
}
String exprType = n.f5.accept(this, argu); // its type must be the same as the typeOfArray
String typeOfArray = arrayType(idType);
if (typeOfArray == null){
throw new Exception("ArrayAssignmentStatement: ID IS NOT AN ARRAY"+"("+Main.ST.getCurrentMethodName()+")");
throw new Exception("ArrayAssignmentStatement: id is not an array"+"("+Main.ST.getCurrentMethodName()+")");
}else{
if (exprType != typeOfArray){
if (!typeOfArray.equals(exprType)){
throw new Exception("ArrayAssignmentStatement: id and expr not the same type"+"("+Main.ST.getCurrentMethodName()+")");
}
return typeOfArray;
Expand All @@ -303,7 +322,7 @@ public String visit(IfStatement n, Void argu) throws Exception {
String _ret=null;
n.f0.accept(this, argu);
n.f1.accept(this, argu);
if (n.f2.accept(this, argu)!="boolean"){
if (!"boolean".equals(n.f2.accept(this, argu))){
throw new Exception("Not a boolean in if expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f3.accept(this, argu);
Expand All @@ -325,7 +344,7 @@ public String visit(WhileStatement n, Void argu) throws Exception {
String _ret=null;
n.f0.accept(this, argu);
n.f1.accept(this, argu);
if (n.f2.accept(this, argu)!="boolean"){
if (!"boolean".equals(n.f2.accept(this, argu))){
throw new Exception("Not a boolean in while expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f3.accept(this, argu);
Expand All @@ -340,12 +359,12 @@ public String visit(WhileStatement n, Void argu) throws Exception {
@Override
public String visit(AndExpression n, Void argu) throws Exception {
String _ret=null;
if (n.f0.accept(this, argu) !="boolean"){
throw new Exception("Not a boolean in end expression"+"("+Main.ST.getCurrentMethodName()+")");
if (!"boolean".equals(n.f0.accept(this, argu))){
throw new Exception("Not a boolean in AND expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
if (n.f2.accept(this, argu) !="boolean"){
throw new Exception("Not a boolean in end expression"+"("+Main.ST.getCurrentMethodName()+")");
if (!"boolean".equals(n.f2.accept(this, argu))){
throw new Exception("Not a boolean in AND expression"+"("+Main.ST.getCurrentMethodName()+")");
}
return "boolean";
}
Expand All @@ -358,11 +377,11 @@ public String visit(AndExpression n, Void argu) throws Exception {
@Override
public String visit(CompareExpression n, Void argu) throws Exception {
String _ret=null;
if (n.f0.accept(this, argu) != "int"){
if (!"int".equals(n.f0.accept(this, argu))){
throw new Exception("Not an integer in LESS expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
if (n.f2.accept(this, argu) != "int"){
if (!"int".equals(n.f2.accept(this, argu))){
throw new Exception("Not an integer in LESS expression"+"("+Main.ST.getCurrentMethodName()+")");
}
return "boolean";
Expand All @@ -378,11 +397,11 @@ public String visit(PlusExpression n, Void argu) throws Exception {
String _ret=null;
String f0Str = n.f0.accept(this, argu);
String f2Str = n.f2.accept(this, argu);
if (f0Str != "int"){
if (!"int".equals(f0Str)){
throw new Exception("Not an integer in PLUS expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
if (f2Str != "int"){
if (!"int".equals(f2Str)){
throw new Exception("Not an integer in PLUS expression"+"("+Main.ST.getCurrentMethodName()+")");
}
return "int";
Expand All @@ -395,11 +414,11 @@ public String visit(PlusExpression n, Void argu) throws Exception {
@Override
public String visit(MinusExpression n, Void argu) throws Exception {
String _ret=null;
if (n.f0.accept(this, argu) != "int"){
if (!"int".equals(n.f0.accept(this, argu))){
throw new Exception("Not an integer in MINUS expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
if (n.f2.accept(this, argu) != "int"){
if (!"int".equals(n.f2.accept(this, argu))){
throw new Exception("Not an integer in MINUS expression"+"("+Main.ST.getCurrentMethodName()+")");
}
return "int";
Expand All @@ -413,11 +432,11 @@ public String visit(MinusExpression n, Void argu) throws Exception {
@Override
public String visit(TimesExpression n, Void argu) throws Exception {
String _ret=null;
if (n.f0.accept(this, argu) != "int"){
if (!"int".equals(n.f0.accept(this, argu))){
throw new Exception("Not an integer in TIMES expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
if (n.f2.accept(this, argu) != "int"){
if (!"int".equals(n.f2.accept(this, argu))){
throw new Exception("Not an integer in TIMES expression"+"("+Main.ST.getCurrentMethodName()+")");
}
return "int";
Expand All @@ -433,14 +452,15 @@ public String visit(TimesExpression n, Void argu) throws Exception {
public String visit(ArrayLookup n, Void argu) throws Exception {
String _ret=null;
String arrayType = n.f0.accept(this, argu);
if (arrayType != "boolean[]" && arrayType != "int[]"){
throw new Exception("Not an array identigier in ArrayLookup expression"+"("+Main.ST.getCurrentMethodName()+")");
if (!"boolean[]".equals(arrayType) && !"int[]".equals(arrayType)){
throw new Exception("Not an array in ArrayLookup expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
if (n.f2.accept(this, argu) != "int"){
if (!"int".equals(n.f2.accept(this, argu))){
throw new Exception("Not an integer in ArrayLookup expression"+"("+Main.ST.getCurrentMethodName()+")");
}
if (arrayType=="int[]") return "int";
Main.ST.setArrayLookupType(n, arrayType);
if ("int[]".equals(arrayType)) return "int";
return "boolean";
}

Expand All @@ -453,8 +473,8 @@ public String visit(ArrayLookup n, Void argu) throws Exception {
public String visit(ArrayLength n, Void argu) throws Exception {
String _ret=null;
String arrayType = n.f0.accept(this, argu);
if (arrayType != "int[]" && arrayType != "boolean[]"){
throw new Exception("Not an array id in ArrayLength expression"+"("+Main.ST.getCurrentMethodName()+")");
if (!"int[]".equals(arrayType) && !"boolean[]".equals(arrayType)){
throw new Exception("Not an array in ArrayLength expression"+"("+Main.ST.getCurrentMethodName()+")");
}
n.f1.accept(this, argu);
n.f2.accept(this, argu);
Expand Down Expand Up @@ -557,7 +577,11 @@ public String visit(PrimaryExpression n, Void argu) throws Exception {
*/
@Override
public String visit(IntegerLiteral n, Void argu) throws Exception {
n.f0.accept(this, argu);
try {
Integer.parseInt(n.f0.toString());
} catch (NumberFormatException ex) {
throw new Exception("IntegerLiteral: " + n.f0.toString() + " is out of range"+"("+Main.ST.getCurrentMethodName()+")");
}
return "int";
}

Expand Down Expand Up @@ -601,8 +625,8 @@ public String visit(ThisExpression n, Void argu) throws Exception {
public String visit(IntegerArrayAllocationExpression n, Void argu) throws Exception {
String _ret=null;
String f3type = n.f3.accept(this, argu);
if (f3type != "int"){
throw new Exception("Array size is not na integer"+"("+Main.ST.getCurrentMethodName()+")");
if (!"int".equals(f3type)){
throw new Exception("Array size is not an integer"+"("+Main.ST.getCurrentMethodName()+")");
}
return "int[]";
}
Expand All @@ -617,8 +641,8 @@ public String visit(IntegerArrayAllocationExpression n, Void argu) throws Except
public String visit(BooleanArrayAllocationExpression n, Void argu) throws Exception {
String _ret=null;
String f3type = n.f3.accept(this, argu);
if (f3type != "int"){
throw new Exception("Array size is not na integer"+"("+Main.ST.getCurrentMethodName()+")");
if (!"int".equals(f3type)){
throw new Exception("Array size is not an integer"+"("+Main.ST.getCurrentMethodName()+")");
}
return "boolean[]";
}
Expand All @@ -639,7 +663,7 @@ public String visit(AllocationExpression n, Void argu) throws Exception {
String className = n.f1.accept(this, argu);
this.inClass = false;
if (!Main.ST.existsClass(className)){
throw new Exception("Class+ "+className+" does not exist"+"("+Main.ST.getCurrentMethodName()+")");
throw new Exception("Class "+className+" does not exist"+"("+Main.ST.getCurrentMethodName()+")");
}
return className;
}
Expand All @@ -649,10 +673,17 @@ public String visit(AllocationExpression n, Void argu) throws Exception {
@Override
public String visit(Identifier n, Void argu) throws Exception {
String idName = n.f0.toString();
if (this.inClass == false){
return Main.ST.lookup(idName);
}
return idName;
if (this.inClass){
return idName;
}
String type = Main.ST.lookup(idName);
if (type == null){
throw new Exception("Undefined identifier: " + idName + "("+Main.ST.getCurrentMethodName()+")");
}
if (type.equals(SymbolTable.MAIN_ARGS_TYPE)){
throw new Exception("The argument of main cannot be used: " + idName + "("+Main.ST.getCurrentMethodName()+")");
}
return type;
}

/**
Expand All @@ -663,7 +694,7 @@ public String visit(Identifier n, Void argu) throws Exception {
public String visit(NotExpression n, Void argu) throws Exception {
String _ret=null;
n.f0.accept(this, argu);
if (n.f1.accept(this, argu) != "boolean"){
if (!"boolean".equals(n.f1.accept(this, argu))){
throw new Exception("NotExpression: primary expression is not a boolean"+"("+Main.ST.getCurrentMethodName()+")");
}
return "boolean";
Expand Down
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Builds the MiniJava compiler and includes clang to compile/run the generated LLVM IR.
# clang 14 (Ubuntu 22.04) is used because the generated IR uses typed pointers (i8*),
# which newer LLVM versions no longer support.
FROM eclipse-temurin:17-jdk-jammy

RUN apt-get update \
&& apt-get install -y --no-install-recommends clang make \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .
RUN make compile

CMD ["./run_tests.sh"]
Loading
Loading