diff --git a/src/main/java/abstractSyntaxTree/Class/FieldDecl.java b/src/main/java/abstractSyntaxTree/Class/FieldDecl.java index baa0f78..56416a5 100644 --- a/src/main/java/abstractSyntaxTree/Class/FieldDecl.java +++ b/src/main/java/abstractSyntaxTree/Class/FieldDecl.java @@ -12,6 +12,7 @@ import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; public class FieldDecl extends AbstractType implements Node { @@ -43,18 +44,13 @@ public class FieldDecl extends AbstractType implements Node { //write field table } - public TypeCheckResult typeCheck(HashMap>>> methodContext, HashMap> typeContext, List fieldsOrMethods) throws Exception { - return null; - } - public void codeGen(ClassWriter cw) { - //TODO: Do we have fields with initial values? + //TODO: Do we have fields with initial values? --> No dont think so --> assign FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, identifier, getFieldDescriptor(), null, null); fv.visitEnd(); } private String getFieldDescriptor() { - //TODO: Maybe we have to check for arrays? switch (type) { case "int": return "I"; @@ -66,4 +62,13 @@ public class FieldDecl extends AbstractType implements Node { return "L" + type + ";"; } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FieldDecl fieldDecl = (FieldDecl) o; + return ( Objects.equals(type, fieldDecl.type) + && Objects.equals(identifier, fieldDecl.identifier)); + } } diff --git a/src/main/java/abstractSyntaxTree/Class/MethodDecl.java b/src/main/java/abstractSyntaxTree/Class/MethodDecl.java index 2707e9f..fc074c7 100644 --- a/src/main/java/abstractSyntaxTree/Class/MethodDecl.java +++ b/src/main/java/abstractSyntaxTree/Class/MethodDecl.java @@ -9,10 +9,7 @@ import abstractSyntaxTree.Statement.BlockStatement; import abstractSyntaxTree.Statement.IStatement; import org.objectweb.asm.*; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Stack; +import java.util.*; public class MethodDecl implements Node { @@ -25,7 +22,8 @@ public class MethodDecl implements Node { public String returnType; public BlockStatement codeBlock; - public HashMap localVars; + //TODO: Can this be a linked hash map? --> need it to be to get the index of local variables + public LinkedHashMap localVars; public MethodDecl(String classThatContainsMethod, String returnType, String name, ParameterList parameters, BlockStatement codeBlock){ this.classThatContainsMethod = classThatContainsMethod; @@ -33,7 +31,7 @@ public class MethodDecl implements Node { this.name = name; this.parameters = parameters; this.codeBlock = codeBlock; - this.localVars = new HashMap<>(); + this.localVars = new LinkedHashMap<>(); } public TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext) throws Exception { @@ -119,7 +117,7 @@ public class MethodDecl implements Node { for (Parameter param : parameters.parameterList) { switch (param.type) { case "int" -> descriptor.append("I"); - case "boolean" -> descriptor.append("Z"); + case "bool" -> descriptor.append("Z"); case "char" -> descriptor.append("C"); case "void" -> descriptor.append("V"); default -> { @@ -139,7 +137,7 @@ public class MethodDecl implements Node { } else { switch (returnType) { case "int" -> descriptor.append("I"); - case "boolean" -> descriptor.append("Z"); + case "bool" -> descriptor.append("Z"); case "char" -> descriptor.append("C"); case "void" -> descriptor.append("V"); default -> { @@ -160,4 +158,16 @@ public class MethodDecl implements Node { } return descriptor.toString(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MethodDecl methodDecl = (MethodDecl) o; + return (Objects.equals(name, methodDecl.name) + && Objects.equals(parameters, methodDecl.parameters) + && Objects.equals(returnType, methodDecl.returnType) + && Objects.equals(codeBlock, methodDecl.codeBlock)); + } + } diff --git a/src/main/java/abstractSyntaxTree/Class/RefType.java b/src/main/java/abstractSyntaxTree/Class/RefType.java index c50c817..19bc5f6 100644 --- a/src/main/java/abstractSyntaxTree/Class/RefType.java +++ b/src/main/java/abstractSyntaxTree/Class/RefType.java @@ -10,6 +10,7 @@ import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; public class RefType extends AbstractType implements Node { @@ -84,10 +85,21 @@ public class RefType extends AbstractType implements Node { } for (MethodDecl method : methodDecls) { - // method.codeGen(cw); + method.codeGen(cw, methodContext); } cw.visitEnd(); } + @Override + public boolean equals(Object o) { + System.out.println("Dont forget me ;)"); + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RefType refType = (RefType) o; + return ( Objects.equals(name, refType.name) + && Objects.equals(fieldDecls, refType.fieldDecls) + && Objects.equals(methodDecls, refType.methodDecls) + && Objects.equals(hasMain, refType.hasMain)); + } } diff --git a/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java b/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java index 7b0b2f8..938c503 100644 --- a/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java @@ -8,6 +8,7 @@ import org.objectweb.asm.*; import java.beans.Expression; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Objects; public class BinaryExpression extends AbstractType implements IExpression{ @@ -16,6 +17,12 @@ public class BinaryExpression extends AbstractType implements IExpression{ public IExpression left; public IExpression right; + public BinaryExpression(String operator, IExpression left, IExpression right) { + this.operator = operator; + this.left = left; + this.right = right; + } + @Override public TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext, HashMap localVars) throws Exception { TypeCheckHelper helper = new TypeCheckHelper(); @@ -60,7 +67,7 @@ public class BinaryExpression extends AbstractType implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { // Label for the jump instruction Label operationFalse = new Label(); //Operation is false Label operationTrue = new Label(); //Operation is true @@ -70,88 +77,88 @@ public class BinaryExpression extends AbstractType implements IExpression{ // Bytecode for the binary operation switch (operator) { case "&&": - left.codeGen(mv); + left.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // IFEQ --> "if equals to zero" (false) --> if left exp is false - right.codeGen(mv); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // If right exp is false, jump to the end of the whole expression mv.visitJumpInsn(Opcodes.GOTO, operationTrue); // If it reaches this point, the right exp is true break; case "||": - left.codeGen(mv); + left.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true - right.codeGen(mv); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IFNE, operationTrue); break; case "==": // Keep in mind that only primitive types are allowed in this case (at this time) - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPEQ, operationTrue); // If the two values are equal, jump to the end of the expression break; case "<": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPLT, operationTrue); // Checks only on less than, not equal break; case ">": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal break; case "<=": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPLE, operationTrue); // Checks on less than OR equal break; case ">=": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPGE, operationTrue); // Checks on greater than OR equal break; case "!=": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitJumpInsn(Opcodes.IF_ICMPNE, operationTrue); // Checks on not equal break; case "+": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.IADD); break; case "-": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.ISUB); break; case "*": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.IMUL); break; case "/": - left.codeGen(mv); - right.codeGen(mv); + left.codeGen(mv, typeContext, localVars); + right.codeGen(mv, typeContext, localVars); mv.visitInsn(Opcodes.IDIV); break; diff --git a/src/main/java/abstractSyntaxTree/Expression/IExpression.java b/src/main/java/abstractSyntaxTree/Expression/IExpression.java index 1245400..2379268 100644 --- a/src/main/java/abstractSyntaxTree/Expression/IExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/IExpression.java @@ -1,16 +1,18 @@ package abstractSyntaxTree.Expression; import TypeCheck.TypeCheckResult; +import abstractSyntaxTree.Node; import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; import java.util.HashMap; +import java.util.LinkedHashMap; -public interface IExpression { +public interface IExpression extends Node { // typeCheck method //TypeCheckResult typeCheck() throws Exception; TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext, HashMap localVars) throws Exception; // visit method for code generation - void codeGen(MethodVisitor mv) throws Exception; + void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception; } diff --git a/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java b/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java index 8a63a44..3e0daf2 100644 --- a/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/InstVarExpression.java @@ -5,15 +5,17 @@ import abstractSyntaxTree.Class.RefType; import abstractSyntaxTree.Parameter.ParameterList; import jdk.jshell.spi.ExecutionControl; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import java.util.HashMap; +import java.util.LinkedHashMap; public class InstVarExpression implements IExpression{ //TODO: We have to decide upon more parameters and where they come from, for // example here we need the index of the field, the class reference and the field name - private RefType classRef; - private String fieldName; + public RefType classRef; + public String fieldName; public InstVarExpression(RefType classRef, String fieldName){ this.classRef = classRef; @@ -32,11 +34,33 @@ public class InstVarExpression implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { - throw new ExecutionControl.NotImplementedException("CodeGen not implemented for InstVarExpression"); + // typeContext: (ClassName, (FieldName, FieldType)) + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { + // Load "this" onto the stack + mv.visitVarInsn(Opcodes.ALOAD, 0); - //ALOAD the index of the var - //GETFIELD the field - //visitFieldInsn(Opcodes.GETFIELD, "class reference", "field name", type); + //Get the field information + String fieldType = typeContext.get(classRef.name).get(fieldName); + + String fieldDescriptor; + + switch (fieldType) { + case "int": + fieldDescriptor = "I"; + break; + case "boolean": + fieldDescriptor = "Z"; + break; + case "char": + fieldDescriptor = "C"; + break; + default: + //TODO: We need the fully qualified name of the class here in field type + fieldDescriptor = "L" + fieldType + ";"; + break; + } + + // Load the variable onto the stack + mv.visitFieldInsn(Opcodes.GETFIELD, classRef.name, fieldName, fieldDescriptor); } } diff --git a/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java b/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java index cc8ef3c..da3f7ea 100644 --- a/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java +++ b/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java @@ -2,6 +2,10 @@ package abstractSyntaxTree.Expression; import TypeCheck.TypeCheckHelper; import TypeCheck.TypeCheckResult; +import org.objectweb.asm.*; + +import java.util.HashMap; +import java.util.LinkedHashMap; import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; @@ -24,7 +28,41 @@ public class LocalVarIdentifier implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { + // Check if the variable is in the list of local variables + String type = localVars.get(identifier); + if (type == null){ + throw new Exception("Variable " + identifier + " not declared"); + } + // Load the variable onto the stack + int index = -1; + int counter = 0; + for (String key : localVars.keySet()){ + if (key.equals(identifier)){ + index = counter; + break; + } + counter++; + } + + if (index == -1){ + throw new Exception("Variable " + identifier + " not found"); + } + + // Load the variable onto the stack + switch (type){ + case "int": + mv.visitVarInsn(Opcodes.ILOAD, index); + break; + case "bool": + mv.visitVarInsn(Opcodes.ILOAD, index); + break; + case "void": + break; + default: + mv.visitVarInsn(Opcodes.ALOAD, index); + break; + } } } diff --git a/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java b/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java index 69149c4..fed80fe 100644 --- a/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/UnaryExpression.java @@ -4,11 +4,11 @@ import TypeCheck.AbstractType; import TypeCheck.TypeCheckHelper; import TypeCheck.TypeCheckResult; import abstractSyntaxTree.Datatype.IDatatype; -import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Objects; public class UnaryExpression extends AbstractType implements IExpression{ @@ -18,7 +18,6 @@ public class UnaryExpression extends AbstractType implements IExpression{ this.operator = operator; this.operand = operand; } - @Override public TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext, HashMap localVars) throws Exception { TypeCheckResult result = new TypeCheckResult(); @@ -50,7 +49,7 @@ public class UnaryExpression extends AbstractType implements IExpression{ } @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { operand.codeGen(mv); diff --git a/src/main/java/abstractSyntaxTree/Program.java b/src/main/java/abstractSyntaxTree/Program.java index aa6d76b..58c1d24 100644 --- a/src/main/java/abstractSyntaxTree/Program.java +++ b/src/main/java/abstractSyntaxTree/Program.java @@ -104,4 +104,14 @@ public class Program implements Node { } */ } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Program program = (Program) o; + System.out.println(classes); + System.out.println(program.classes); + return (Objects.equals(classes, program.classes)); + } } \ No newline at end of file diff --git a/src/main/java/abstractSyntaxTree/Statement/IStatement.java b/src/main/java/abstractSyntaxTree/Statement/IStatement.java index 2225532..1ee7271 100644 --- a/src/main/java/abstractSyntaxTree/Statement/IStatement.java +++ b/src/main/java/abstractSyntaxTree/Statement/IStatement.java @@ -6,11 +6,14 @@ import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; public interface IStatement extends Node { TypeCheckResult typeCheck(HashMap>> methodContext, HashMap> typeContext, HashMap localVars) throws Exception; - void codeGen(MethodVisitor mv, HashMap localVars) throws Exception; + void codeGen(MethodVisitor mv, LinkedHashMap localVars) throws Exception; + + void codeGen(MethodVisitor mv, LinkedHashMap localVars, HashMap> typeContext) throws Exception; } diff --git a/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java b/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java index 4fd73a4..c53b3af 100644 --- a/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java +++ b/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java @@ -4,6 +4,7 @@ import TypeCheck.TypeCheckHelper; import TypeCheck.TypeCheckResult; import abstractSyntaxTree.Parameter.ParameterList; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.Arrays; @@ -30,6 +31,25 @@ public class LocalVarDecl implements IStatement{ @Override public void codeGen(MethodVisitor mv, HashMap localVars) throws Exception { + localVars.put(identifier, type); + // Set a default value for the variable --> less problems + int index = localVars.size()-1; + + switch (type){ + case "int": + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ISTORE, index); + break; + case "bool": + mv.visitInsn(Opcodes.ICONST_0); + mv.visitVarInsn(Opcodes.ISTORE, index); + break; + case "void": + break; + default: + mv.visitInsn(Opcodes.ACONST_NULL); + mv.visitVarInsn(Opcodes.ASTORE, index); + } } } diff --git a/src/main/java/abstractSyntaxTree/Statement/ReturnStatement.java b/src/main/java/abstractSyntaxTree/Statement/ReturnStatement.java index e4a2530..c19beb7 100644 --- a/src/main/java/abstractSyntaxTree/Statement/ReturnStatement.java +++ b/src/main/java/abstractSyntaxTree/Statement/ReturnStatement.java @@ -39,6 +39,7 @@ public class ReturnStatement extends AbstractType implements IStatement{ if (expression != null) { expression.codeGen(mv); //Get the Type of the expression + //TODO: Resolve how do we get the type of the expression String type = expression.typeCheck(null, null, null).type; if (type.equals("int") || type.equals("bool") || type.equals("char")) { diff --git a/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java b/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java index 43045ba..b2f1e53 100644 --- a/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java +++ b/src/main/java/abstractSyntaxTree/StatementExpression/AssignStatementExpression.java @@ -11,12 +11,13 @@ import abstractSyntaxTree.Statement.IStatement; import org.objectweb.asm.*; import java.util.HashMap; -import java.util.Objects; +import java.util.LinkedHashMap; public class AssignStatementExpression extends AbstractType implements IExpression, IStatement { public String operator; public IExpression left; public IExpression right; + private InstVarExpression instVar; public AssignStatementExpression(String operator, IExpression leftExpression, IExpression rightExpression){ this.operator = operator; @@ -51,9 +52,9 @@ public class AssignStatementExpression extends AbstractType implements IExpressi @Override public void codeGen(MethodVisitor mv, HashMap localVars) throws Exception { -// if (left instanceof VarRefExpression varRef) { -// -// } + if (left instanceof VarRefExpression varRef) { + + } } @@ -62,9 +63,48 @@ public class AssignStatementExpression extends AbstractType implements IExpressi } @Override - public void codeGen(MethodVisitor mv) throws Exception { - left.codeGen(mv); - right.codeGen(mv); + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { + //TODO: Do we need the value on the stack after assigning it? + //TODO: WE do not differentiate between InstanceVar and FieldVar + + // Call the codeGen on the right expression which will push the value of the right expression onto the stack + right.codeGen(mv, typeContext, localVars); + + if (left instanceof LocalVarIdentifier) { + LocalVarIdentifier localVar = (LocalVarIdentifier) left; + String varName = localVar.getIdentifier(); + + //Get the index of the local variable + int index = -1; + int counter = 0; + for (String key : localVars.keySet()){ + if (key.equals(varName)){ + index = counter; + break; + } + counter++; + } + + if (index == -1){ + throw new Exception("Variable " + varName + " not found"); + } + + String type = localVars.get(localVar.getIdentifier()); + switch (type) { + case "int": + case "bool": + mv.visitVarInsn(Opcodes.ISTORE, index); + break; + default: + mv.visitVarInsn(Opcodes.ASTORE, index); + break; + } + + } else if (left instanceof InstVarExpression){ + instVar = (InstVarExpression) left; + + // Load "this" onto the stack + mv.visitVarInsn(Opcodes.ALOAD, 0); // if (left instanceof VarRefExpression varRef) { // //TODO: Implement the handling of a variable reference --> I need a list of local variables diff --git a/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java b/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java index 3fe48b6..7a8498c 100644 --- a/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java +++ b/src/main/java/abstractSyntaxTree/StatementExpression/MethodCallStatementExpression.java @@ -12,6 +12,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; public class MethodCallStatementExpression extends AbstractType implements IExpression, IStatement { @@ -50,27 +51,44 @@ public class MethodCallStatementExpression extends AbstractType implements IExpr return null; } - @Override - public void codeGen(MethodVisitor mv, HashMap localVars) throws Exception { - - } //Errors occur due to the change in parameter in the RefType class + // I need the methodContext here to get the method descriptor @Override - public void codeGen(MethodVisitor mv) throws Exception { + public void codeGen(MethodVisitor mv, HashMap> typeContext, LinkedHashMap localVars) throws Exception { //Generate Bytecode for the receiver if(classThatHasTheMethodIfNotThis != null){ - // classThatHasTheMethodIfNotThis.codeGen(new ClassWriter(ClassWriter.COMPUTE_FRAMES)); + //TODO: classThatHasTheMethodIfNotThis must be an object --> instance of the class not the class itself + //classThatHasTheMethodIfNotThis.codeGen(); + + String descriptor; + List methodDecls = thisClass.methodDecls; + for (MethodDecl methodDecl : methodDecls) { + if (methodDecl.name.equals(methodName)) { + //Get the method descriptor + descriptor = methodDecl.getMethodDescriptor(methodContext); + } + } + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, classThatHasTheMethodIfNotThis.name, methodName, descriptor, false); + } else { + // Load this onto the stack mv.visitVarInsn(Opcodes.ALOAD, 0); } for (IExpression argument : arguments) { - argument.codeGen(mv); + argument.codeGen(mv, typeContext, localVars); } - //We need the class reference and the return type of the method - //mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, thisClass.name, methodName, return type); - + // Get the method descriptor + String descriptor; + List methodDecls = thisClass.methodDecls; + for (MethodDecl methodDecl : methodDecls) { + if (methodDecl.name.equals(methodName)) { + //Get the method descriptor + descriptor = methodDecl.getMethodDescriptor(methodContext); + } + } + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, thisClass.name, methodName, descriptor, false); } } diff --git a/src/main/java/astGenerator/ASTGenerator.java b/src/main/java/astGenerator/ASTGenerator.java index 90797df..183013d 100644 --- a/src/main/java/astGenerator/ASTGenerator.java +++ b/src/main/java/astGenerator/ASTGenerator.java @@ -4,6 +4,7 @@ import abstractSyntaxTree.Class.FieldDecl; import abstractSyntaxTree.Class.MethodDecl; import abstractSyntaxTree.Class.RefType; import abstractSyntaxTree.Expression.BinaryExpression; +import abstractSyntaxTree.Expression.IExpression; import abstractSyntaxTree.Node; import abstractSyntaxTree.Parameter.Parameter; import abstractSyntaxTree.Parameter.ParameterList; @@ -114,7 +115,8 @@ public class ASTGenerator extends DecafBaseVisitor { @Override public Node visitReturnStmt(DecafParser.ReturnStmtContext ctx) { - return new ReturnStatement(new BinaryExpression()); + Node expression = visitExpression(ctx.expression()); + return new ReturnStatement((IExpression) expression); } @Override @@ -132,22 +134,25 @@ public class ASTGenerator extends DecafBaseVisitor { if (ctx.elseStmt() != null) { return visitIfStmt(ctx.ifStmt()); } else { + Node expression = visitExpression(ctx.ifStmt().expression()); Node ifStatement = visitStatement(ctx.ifStmt().statement()); Node elseStatement = visitStatement(ctx.elseStmt().statement()); - return new IfElseStatement(new BinaryExpression(), (IStatement) ifStatement, (IStatement) elseStatement); + return new IfElseStatement((IExpression) expression, (IStatement) ifStatement, (IStatement) elseStatement); } } @Override public Node visitIfStmt(DecafParser.IfStmtContext ctx) { + Node expression = visitExpression(ctx.expression()); Node statement = visitStatement(ctx.statement()); - return new IfStatement(new BinaryExpression(), (IStatement) statement); + return new IfStatement((IExpression) expression, (IStatement) statement); } @Override public Node visitWhileStmt(DecafParser.WhileStmtContext ctx) { + Node expression = visitExpression(ctx.expression()); Node statement = visitStatement(ctx.statement()); - return new WhileStatement(new BinaryExpression(), (IStatement) statement); + return new WhileStatement((IExpression) expression, (IStatement) statement); } @Override @@ -167,7 +172,6 @@ public class ASTGenerator extends DecafBaseVisitor { return null; } - @Override public Node visitAssign(DecafParser.AssignContext ctx) { return new AssignStatementExpression("", null, null); @@ -182,4 +186,79 @@ public class ASTGenerator extends DecafBaseVisitor { public Node visitNewDecl(DecafParser.NewDeclContext ctx) { return super.visitNewDecl(ctx); } + + @Override + public Node visitExpression(DecafParser.ExpressionContext ctx) { + if (ctx.subExpression() != null) { + + } else if (ctx.binaryExpr() != null) { + return visitBinaryExpr(ctx.binaryExpr()); + } + return null; + } + + @Override + public Node visitBinaryExpr(DecafParser.BinaryExprContext ctx) { + if (ctx.calcExpr() != null) { + return visitCalcExpr(ctx.calcExpr()); + } else if (ctx.nonCalcExpr() != null) { + return visitNonCalcExpr(ctx.nonCalcExpr()); + } else if (ctx.value() != null) { + //todo + } else if (ctx.binaryExpr() != null) { + //todo + } + return null; + } + + @Override + public Node visitCalcExpr(DecafParser.CalcExprContext ctx) { + if (ctx.calcExpr() != null) { + Node left = visitCalcExpr(ctx.calcExpr()); + Node right = visitDotExpr(ctx.dotExpr()); + return new BinaryExpression(ctx.LineOperator().getText(), (IExpression) left, (IExpression) right); + } else { + visitDotExpr(ctx.dotExpr()); + } + return null; + } + + @Override + public Node visitDotExpr(DecafParser.DotExprContext ctx) { + if (ctx.dotExpr() != null) { + Node left = visitDotExpr(ctx.dotExpr()); + Node right = visitDotSubExpr(ctx.dotSubExpr()); + return new BinaryExpression(ctx.DotOperator().getText(), (IExpression) left, (IExpression) right); + } else { + visitDotSubExpr(ctx.dotSubExpr()); + } + return null; + } + + //todo + @Override + public Node visitDotSubExpr(DecafParser.DotSubExprContext ctx) { + return super.visitDotSubExpr(ctx); + } + + @Override + public Node visitNonCalcExpr(DecafParser.NonCalcExprContext ctx) { + String operator; + if(ctx.nonCalcOperator().LogicalOpertor() != null) { + operator = ctx.nonCalcOperator().LogicalOpertor().getText(); + } else { + operator = ctx.nonCalcOperator().ComparisonOperator().getText(); + } + Node left = visitSubExpression(ctx.subExpression()); + Node right = visitExpression(ctx.expression()); + return new BinaryExpression(operator, (IExpression) left, (IExpression) right); + } + + @Override + public Node visitSubExpression(DecafParser.SubExpressionContext ctx) { + if (ctx.subExpression() != null) { + visitSubExpression(ctx.subExpression()); + } + return null; + } } diff --git a/src/test/java/AST/AstComparer.java b/src/test/java/AST/AstComparer.java new file mode 100644 index 0000000..3ef2ce9 --- /dev/null +++ b/src/test/java/AST/AstComparer.java @@ -0,0 +1,44 @@ +package AST; + +import ASTs.emptyClassAST; +import abstractSyntaxTree.Program; +import static org.junit.Assert.assertEquals; + +import abstractSyntaxTree.Statement.BlockStatement; +import gen.DecafLexer; +import gen.DecafParser; +import org.antlr.v4.runtime.tree.ParseTree; +import org.junit.Test; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.antlr.v4.runtime.*; +import abstractSyntaxTree.ASTGenerator; + +public class AstComparer { + + private static String BASE_DIR; + public AstComparer(String base_directory){ + BASE_DIR = base_directory + "/"; + } + + + public void astComparison(String sourceFilePath, Program expectedAST) throws Exception { + // Read the source file + String content = new String(Files.readAllBytes(Paths.get(BASE_DIR + sourceFilePath))); + CharStream codeCharStream = CharStreams.fromString(content); + + // Setup Lexer and Parser + DecafLexer lexer = new DecafLexer(codeCharStream); + CommonTokenStream tokens = new CommonTokenStream(lexer); + DecafParser parser = new DecafParser(tokens); + ParseTree tree = parser.program(); + + // Generate AST + ASTGenerator generator = new ASTGenerator(); + Program generatedAST = (Program) generator.visit(tree); + + // Assert that both ASTs are equal + assertEquals("The generated AST does not match the expected AST", expectedAST, generatedAST); + } + +} \ No newline at end of file diff --git a/src/test/java/AST/testAll.java b/src/test/java/AST/testAll.java new file mode 100644 index 0000000..5ec3665 --- /dev/null +++ b/src/test/java/AST/testAll.java @@ -0,0 +1,28 @@ +package AST; +import ASTs.emptyClassAST; +import abstractSyntaxTree.Program; +import org.junit.Test; + +import java.io.File; + +public class testAll { + + AstComparer astComparer; + public testAll() + { + this.astComparer = new AstComparer("src/test/resources"); + } + + @Test + public void TestAstGeneration() + { + System.out.println("Current working directory: " + new File(".").getAbsolutePath()); + Program testEmptyClassAST = emptyClassAST.getEmptyProgramm(); + + try { + astComparer.astComparison("basicClasses/emptyClass.java", testEmptyClassAST); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/ASTs/emptyClassAST.java b/src/test/java/ASTs/emptyClassAST.java new file mode 100644 index 0000000..e176d2d --- /dev/null +++ b/src/test/java/ASTs/emptyClassAST.java @@ -0,0 +1,24 @@ +package ASTs; + +import abstractSyntaxTree.*; +import abstractSyntaxTree.Class.FieldDecl; +import abstractSyntaxTree.Class.MethodDecl; +import abstractSyntaxTree.Class.RefType; + +import java.util.ArrayList; +import java.util.List; + +public class emptyClassAST { + + + public static Program getEmptyProgramm() { + List emptyFieldDeclList = new ArrayList<>(); + List emptyMethodDeclList = new ArrayList<>(); + RefType emptyClass = new RefType("emptyClass", emptyFieldDeclList, emptyMethodDeclList, false); + List classes = new ArrayList<>(); + classes.add(emptyClass); + + return (new Program(classes)); + } + +} diff --git a/src/test/java/ASTs/emptyClassWithConstructorAST.java b/src/test/java/ASTs/emptyClassWithConstructorAST.java new file mode 100644 index 0000000..8d288c2 --- /dev/null +++ b/src/test/java/ASTs/emptyClassWithConstructorAST.java @@ -0,0 +1,34 @@ +package ASTs; + +import abstractSyntaxTree.Class.FieldDecl; +import abstractSyntaxTree.Class.MethodDecl; +import abstractSyntaxTree.Class.RefType; +import abstractSyntaxTree.Parameter.Parameter; +import abstractSyntaxTree.Parameter.ParameterList; +import abstractSyntaxTree.Program; +import abstractSyntaxTree.Statement.BlockStatement; +import abstractSyntaxTree.Statement.IStatement; + +import java.util.ArrayList; +import java.util.List; + +public class emptyClassWithConstructorAST extends Program { + public emptyClassWithConstructorAST() + { + super(staticClasses()); + } + private static List staticClasses() { + List fieldDeclList = new ArrayList<>(); + + ParameterList emptyParameterList = new ParameterList(new ArrayList<>()); + List emptyIStatementList = new ArrayList<>(); + BlockStatement emptyBlockStatement = new BlockStatement(emptyIStatementList, "void"); + MethodDecl constructor = new MethodDecl("emptyClassWithConstructor", "null", "emptyClassWithConstructor", emptyParameterList, emptyBlockStatement); + List methodDeclList = new ArrayList<>(); + methodDeclList.add(constructor); + RefType emptyClass = new RefType("emptyClass", fieldDeclList, methodDeclList, false); + List classes = new ArrayList<>(); + classes.add(emptyClass); + return classes; + } +} diff --git a/src/test/java/ASTs/fourClassesAST.java b/src/test/java/ASTs/fourClassesAST.java new file mode 100644 index 0000000..5a2569c --- /dev/null +++ b/src/test/java/ASTs/fourClassesAST.java @@ -0,0 +1,80 @@ +package ASTs; + +import abstractSyntaxTree.Class.FieldDecl; +import abstractSyntaxTree.Class.MethodDecl; +import abstractSyntaxTree.Class.RefType; +import abstractSyntaxTree.Expression.IExpression; +import abstractSyntaxTree.Expression.InstVarExpression; +import abstractSyntaxTree.Parameter.Parameter; +import abstractSyntaxTree.Parameter.ParameterList; +import abstractSyntaxTree.Program; +import abstractSyntaxTree.Statement.BlockStatement; +import abstractSyntaxTree.Statement.IStatement; +import abstractSyntaxTree.Statement.ReturnStatement; +import abstractSyntaxTree.StatementExpression.AssignStatementExpression; +import abstractSyntaxTree.StatementExpression.NewStatementExpression; + +import java.util.ArrayList; +import java.util.List; + +/* +public class fourClassesAST extends Program { + + public fourClassesAST() + { + super(staticClasses()); + } + private static List staticClasses() { + + /////////// Classes /////////// + + ///////// Class: FourClasses ///////// + + /////// Fields /////// + List fieldDeclList = new ArrayList<>(); + + ParameterList emptyParameterList = new ParameterList(new ArrayList<>()); + + /////// Methods /////// + + // Main // + + List emptyIStatementList = new ArrayList<>(); + BlockStatement emptyBlockStatement = new BlockStatement(emptyIStatementList, "void"); + MethodDecl constructor = new MethodDecl("emptyClassWithConstructor", "null", "emptyClassWithConstructor", emptyParameterList, emptyBlockStatement); + + //IStatement + List fourClassesMainBlockIstatements = new ArrayList<>(); + + IExpression atntiLeft = new InstVarExpression("Test", "t"); + IExpression atntiRight = new NewStatementExpression(); + AssignStatementExpression atnti = new AssignStatementExpression(atntiLeft, "=", ); + + + ReturnStatement fcmbiReturn = new ReturnStatement(); + + fourClassesMainBlockIstatements.add(atnti); + //BlockStatement + BlockStatement fourClassesMainBlock = new BlockStatement(fourClassesMainBlockIstatements, "int"); + + //Parameter + Parameter intI = new Parameter("int", "i"); + List fourClassesMainParameterList = new ArrayList<>(); + + ParameterList fourClassesMainParameters = new ParameterList(fourClassesMainParameterList); + + MethodDecl fourClassesMain = new MethodDecl("fourClasses", "int", "main", fourClassesMainParameters, fourClassesMainBlock); + List MethodDeclList = new ArrayList<>(); + + RefType emptyClass = new RefType("emptyClass", fieldDeclList, MethodDeclList, false); + + + + + + + List classes = new ArrayList<>(); + classes.add(emptyClass); + return classes; + } +}*/ diff --git a/src/test/java/Tokens/JavaLexerTest.java b/src/test/java/Tokens/JavaLexerTest.java index 12c5ea6..f08e302 100644 --- a/src/test/java/Tokens/JavaLexerTest.java +++ b/src/test/java/Tokens/JavaLexerTest.java @@ -5,7 +5,6 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.Token; -import org.junit.Test; import java.nio.file.Files; import java.nio.file.Paths; @@ -40,8 +39,6 @@ public class JavaLexerTest { }*/ // Compare tokens - //assertEquals("Number of tokens does not match expected", expectedTokens.size(), tokenList.size()); - for (int i = 0; i < tokenList.size(); i++) { Token token = tokenList.get(i); String tokenData = String.format("%s: \"%s\"", lexer.getVocabulary().getSymbolicName(token.getType()), token.getText()); diff --git a/src/test/java/Tokens/TestAll.java b/src/test/java/Tokens/TestAll.java new file mode 100644 index 0000000..17f4b64 --- /dev/null +++ b/src/test/java/Tokens/TestAll.java @@ -0,0 +1,34 @@ +package Tokens; + +import org.junit.Test; + +public class TestAll { + JavaLexerTest testLexer; + public TestAll(){ + testLexer = new JavaLexerTest("src/test/resources"); + } + + private static void main(String[] args){ + TestAll tester = new TestAll(); + } + + @Test + public void testEmptyClass() throws Exception { + testLexer.testTokens("basicClasses/emptyClass.java", "basicClasses/emptyClass.tokens"); + } + + @Test + public void testEmptyClassWithConstructor() throws Exception { + testLexer.testTokens("basicClasses/EmptyClassWithConstructor.java", "basicClasses/EmptyClassWithConstructor.tokens"); + } + + @Test + public void testFourClasses() throws Exception { + testLexer.testTokens("basicClasses/FourClasses.java", "basicClasses/FourClasses.tokens"); + } + + @Test + public void testPublicEmptyClass() throws Exception { + testLexer.testTokens("basicClasses/PublicEmptyClass.java", "basicClasses/PublicEmptyClass.tokens"); + } +}