diff --git a/src/main/java/abstractSyntaxTree/Class/FieldDecl.java b/src/main/java/abstractSyntaxTree/Class/FieldDecl.java index a79fe5d..14d4d39 100644 --- a/src/main/java/abstractSyntaxTree/Class/FieldDecl.java +++ b/src/main/java/abstractSyntaxTree/Class/FieldDecl.java @@ -4,6 +4,7 @@ import TypeCheck.AbstractType; import TypeCheck.TypeCheckException; import TypeCheck.TypeCheckHelper; import TypeCheck.TypeCheckResult; +import abstractSyntaxTree.Expression.IExpression; import abstractSyntaxTree.Node; import abstractSyntaxTree.Program; import org.objectweb.asm.ClassWriter; @@ -18,11 +19,13 @@ import java.util.Objects; public class FieldDecl extends AbstractType implements Node { public String type; // from parser - public String identifier; // from parser + public String identifier;// from parser + public IExpression expression; // value of the field - public FieldDecl(String type, String identifier){ + public FieldDecl(String type, String identifier, IExpression expression){ this.type = type; this.identifier = identifier; + this.expression = expression; } public TypeCheckResult typeCheck(HashMap> typeContext) throws TypeCheckException { @@ -46,7 +49,6 @@ public class FieldDecl extends AbstractType implements Node { } public void codeGen(ClassWriter cw) { - //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(); } diff --git a/src/main/java/abstractSyntaxTree/Class/MethodDecl.java b/src/main/java/abstractSyntaxTree/Class/MethodDecl.java index b412f4a..d836d61 100644 --- a/src/main/java/abstractSyntaxTree/Class/MethodDecl.java +++ b/src/main/java/abstractSyntaxTree/Class/MethodDecl.java @@ -53,12 +53,10 @@ public class MethodDecl implements Node { return result; } - - //TODO: Es wird kein Bytecode für Standardkonstruktoren für die Klassen generiert - //TODO: Stack computing schlägt fehl --> Reihenfolge aufruf? --> Sobald if-else / if / while drin sind? --> vllt auch schon bei MethodenDecl //Need to get the returnType of the method if it is an object // methodContext (class, (returnType, (identifier, parameter))) - public void codeGen(ClassWriter cw, HashMap>> methodContext, HashMap> typeContext) throws Exception { + // typeContext (class, (type, identifier)) + public void codeGen(ClassWriter cw, HashMap>> methodContext, HashMap> typeContext, List fieldDecls) throws Exception { localVars.put("this", classThatContainsMethod); for (Parameter param : parameters.parameterList) { @@ -73,18 +71,40 @@ public class MethodDecl implements Node { //Call the superclass constructor mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false); + + HashMap classFields = typeContext.get(classThatContainsMethod); + + for (Map.Entry entry : classFields.entrySet()) { + String fieldName = entry.getKey(); + for (FieldDecl field : fieldDecls) { + if (field.identifier.equals(fieldName)) { + mv.visitVarInsn(Opcodes.ALOAD, 0); + field.expression.codeGen(mv, localVars, typeContext, methodContext); + descriptor = getFieldDescriptor(field.type); + mv.visitFieldInsn(Opcodes.PUTFIELD, classThatContainsMethod, fieldName, descriptor); + } else { + throw new Exception("Field " + fieldName + " not found"); + } + } + } //Load the parameters onto the stack int localVarIndex = 1; for (Parameter param : parameters.parameterList) { String paramType = param.type; switch(paramType) { - case "int", "boolean", "char" -> mv.visitVarInsn(Opcodes.ILOAD, localVarIndex); - default -> mv.visitVarInsn(Opcodes.ALOAD, localVarIndex); + case "int", "boolean", "char" -> { + mv.visitVarInsn(Opcodes.ILOAD, localVarIndex); + mv.visitVarInsn(Opcodes.ISTORE, localVarIndex); + } + default -> { + mv.visitVarInsn(Opcodes.ALOAD, localVarIndex); + mv.visitVarInsn(Opcodes.ASTORE, localVarIndex); + } } localVarIndex++; } - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", descriptor, false); mv.visitCode(); codeBlock.codeGen(mv, localVars, typeContext, methodContext); @@ -171,6 +191,19 @@ public class MethodDecl implements Node { return descriptor.toString(); } + private String getFieldDescriptor(String type) { + switch (type) { + case "int": + return "I"; + case "boolean": + return "Z"; + case "char": + return "C"; + default: + return "L" + type + ";"; + } + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/abstractSyntaxTree/Class/RefType.java b/src/main/java/abstractSyntaxTree/Class/RefType.java index 76cc208..580aca9 100644 --- a/src/main/java/abstractSyntaxTree/Class/RefType.java +++ b/src/main/java/abstractSyntaxTree/Class/RefType.java @@ -6,6 +6,7 @@ import TypeCheck.TypeCheckResult; import abstractSyntaxTree.Expression.IExpression; import abstractSyntaxTree.Node; import abstractSyntaxTree.Parameter.ParameterList; +import abstractSyntaxTree.Statement.BlockStatement; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; @@ -79,7 +80,7 @@ public class RefType extends AbstractType implements Node { // Method for code generation which iterates over all the field declarations // and method declarations and calls their CodeGen methods - public void codeGen(ClassWriter cw, HashMap>> methodContext, HashMap> typeContext) throws Exception { + public void codeGen(ClassWriter cw, HashMap>> methodContext, HashMap> typeContext) throws Exception { cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name, null, "java/lang/Object", null); @@ -88,8 +89,21 @@ public class RefType extends AbstractType implements Node { field.codeGen(cw); } + boolean hasCustomConstructor = false; for (MethodDecl method : methodDecls) { - method.codeGen(cw, methodContext, typeContext); + if (method.name.equals(name) && method.returnType == null) { + hasCustomConstructor = true; + break; + } + } + + if (!hasCustomConstructor) { + MethodDecl standardConstructor = new MethodDecl(name, null, name, new ParameterList(new ArrayList<>()), new BlockStatement(new ArrayList<>(), "void")); + standardConstructor.codeGen(cw, methodContext, typeContext, fieldDecls); + } + + for (MethodDecl method : methodDecls) { + method.codeGen(cw, methodContext, typeContext, fieldDecls); } cw.visitEnd(); } diff --git a/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java b/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java index 6b775f6..68f58e5 100644 --- a/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java +++ b/src/main/java/abstractSyntaxTree/Expression/BinaryExpression.java @@ -76,105 +76,96 @@ public class BinaryExpression extends AbstractType implements IExpression{ Label expressionEnd = new Label(); //End of the whole expression // Bytecode for the binary operation - switch (operator) { - case "&&": - left.codeGen(mv, localVars, typeContext, methodContext); - mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // IFEQ --> "if equals to zero" (false) --> if left exp is false + if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/")) { + switch (operator) { + case "+" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitInsn(Opcodes.IADD); + } + case "-" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitInsn(Opcodes.ISUB); + } + case "*" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitInsn(Opcodes.IMUL); + } + case "/" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitInsn(Opcodes.IDIV); + } + } + } else { + switch (operator) { + case "&&" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // IFEQ --> "if equals to zero" (false) --> if left exp is false - right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); // If right exp is false, jump to the end of the whole expression + right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IFEQ, operationFalse); - mv.visitJumpInsn(Opcodes.GOTO, operationTrue); // If it reaches this point, the right exp is true - break; + mv.visitJumpInsn(Opcodes.GOTO, operationTrue); // If it reaches this point, the right exp is true + } + case "||" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true - case "||": - left.codeGen(mv, localVars, typeContext, methodContext); - mv.visitJumpInsn(Opcodes.IFNE, operationTrue); // IFNE --> "if not equals to zero" (true) --> if left exp is true + right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IFNE, operationTrue); + } + case "==" -> { + // Keep in mind that only primitive types are allowed in this case (at this time) - right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitJumpInsn(Opcodes.IFNE, operationTrue); - break; + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); - case "==": - // Keep in mind that only primitive types are allowed in this case (at this time) + mv.visitJumpInsn(Opcodes.IF_ICMPEQ, operationTrue); // If the two values are equal, jump to the end of the expression + } + case "<" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IF_ICMPLT, operationTrue); // Checks only on less than, not equal + } + case ">" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitJumpInsn(Opcodes.IF_ICMPEQ, operationTrue); // If the two values are equal, jump to the end of the expression - break; + mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal + } + case "<=" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); - case "<": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IF_ICMPLE, operationTrue); // Checks on less than OR equal + } + case ">=" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitJumpInsn(Opcodes.IF_ICMPLT, operationTrue); // Checks only on less than, not equal - break; + mv.visitJumpInsn(Opcodes.IF_ICMPGE, operationTrue); // Checks on greater than OR equal + } + case "!=" -> { + left.codeGen(mv, localVars, typeContext, methodContext); + right.codeGen(mv, localVars, typeContext, methodContext); - case ">": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); + mv.visitJumpInsn(Opcodes.IF_ICMPNE, operationTrue); // Checks on not equal + } + default -> throw new TypeCheckException("The operator " + operator + " is not known."); + } + mv.visitLabel(operationFalse); + mv.visitInsn(Opcodes.ICONST_0); // Push false on the stack + mv.visitJumpInsn(Opcodes.GOTO, expressionEnd); // Jump to the end of the expression (skip the true push) - mv.visitJumpInsn(Opcodes.IF_ICMPGT, operationTrue); // Checks only on greater than, not equal - break; + mv.visitLabel(operationTrue); + mv.visitInsn(Opcodes.ICONST_1); // Push true on the stack - case "<=": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - - mv.visitJumpInsn(Opcodes.IF_ICMPLE, operationTrue); // Checks on less than OR equal - break; - - case ">=": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - - mv.visitJumpInsn(Opcodes.IF_ICMPGE, operationTrue); // Checks on greater than OR equal - break; - - case "!=": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - - mv.visitJumpInsn(Opcodes.IF_ICMPNE, operationTrue); // Checks on not equal - break; - - case "+": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitInsn(Opcodes.IADD); - break; - - case "-": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitInsn(Opcodes.ISUB); - break; - - case "*": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitInsn(Opcodes.IMUL); - break; - - case "/": - left.codeGen(mv, localVars, typeContext, methodContext); - right.codeGen(mv, localVars, typeContext, methodContext); - mv.visitInsn(Opcodes.IDIV); - break; - - default: - throw new TypeCheckException("The operator " + operator + " is not known."); + mv.visitLabel(expressionEnd); } - - mv.visitLabel(operationFalse); - mv.visitInsn(Opcodes.ICONST_0); // Push false on the stack - mv.visitJumpInsn(Opcodes.GOTO, expressionEnd); // Jump to the end of the expression (skip the true push) - - mv.visitLabel(operationTrue); - mv.visitInsn(Opcodes.ICONST_1); // Push true on the stack - - mv.visitLabel(expressionEnd); } @Override diff --git a/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java b/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java index d098002..772eef6 100644 --- a/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java +++ b/src/main/java/abstractSyntaxTree/Expression/LocalVarIdentifier.java @@ -46,40 +46,60 @@ public class LocalVarIdentifier extends AbstractType implements IExpression{ @Override public void codeGen(MethodVisitor mv, LinkedHashMap localVars, HashMap> typeContext, HashMap>> methodContext) 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"); - } + String type = null; - // Find the index of the variable - int index = -1; - int counter = 0; - for (String key : localVars.keySet()){ - if (key.equals(identifier)){ - index = counter+1; // +1 because the first local variable is at index 1, 0 is used for "this" - break; + // if it's a local variable + if (localVars.containsKey(identifier)) { + type = localVars.get(identifier); + // Find the index of the variable + int index = -1; + int counter = 0; + for (String key : localVars.keySet()) { + if (key.equals(identifier)) { + index = counter + 1; // +1 because the first local variable is at index 1, 0 is used for "this" + break; + } + counter++; } - counter++; - } - if (index == -1){ + if (index == -1) { + throw new Exception("Variable " + identifier + " not found"); + } + + // Load the variable onto the stack + switch (type) { + case "int", "boolean", "char": + mv.visitVarInsn(Opcodes.ILOAD, index); + break; + case "void": + break; + default: + mv.visitVarInsn(Opcodes.ALOAD, index); + break; + } + // If it's a field + } else if (typeContext.get(thisClass).get(identifier) != null){ + type = typeContext.get(thisClass).get(identifier); + + // Load "this" onto the stack + mv.visitVarInsn(Opcodes.ALOAD, 0); + // Get the field from "this" + mv.visitFieldInsn(Opcodes.GETFIELD, thisClass, identifier, getFieldDescriptor(type)); + } else throw new Exception("Variable " + identifier + " not found"); - } + } - // Load the variable onto the stack - switch (type){ + //TODO move this to a helper class and remove the doubled code in MethodDecl and in other places + private String getFieldDescriptor(String type) { + switch (type) { case "int": - mv.visitVarInsn(Opcodes.ILOAD, index); - break; + return "I"; case "boolean": - mv.visitVarInsn(Opcodes.ILOAD, index); - break; - case "void": - break; + return "Z"; + case "char": + return "C"; default: - mv.visitVarInsn(Opcodes.ALOAD, index); - break; + return "L" + type + ";"; } } diff --git a/src/main/java/abstractSyntaxTree/Program.java b/src/main/java/abstractSyntaxTree/Program.java index 1d2a800..3826a24 100644 --- a/src/main/java/abstractSyntaxTree/Program.java +++ b/src/main/java/abstractSyntaxTree/Program.java @@ -42,6 +42,7 @@ public class Program implements Node { typeContext.put(oneClass.name, classVars); // build method context + HashMap> identifierAndMethod = new HashMap<>(); for (MethodDecl methodDecl : oneClass.methodDecls){ if(methodDecl.returnType == null) continue; @@ -52,15 +53,14 @@ public class Program implements Node { methodContext.put(oneClass.name, identifierAndMethod); } - int mainCounter = 0; // check if main exists for(RefType oneClass : classes){ if(oneClass.hasMain) mainCounter++; } -// if(mainCounter != 1) -// throw new TypeCheckException("There is not 1 Main method."); + if(mainCounter != 1) + throw new TypeCheckException("There is not 1 Main method."); // typecheck each class TypeCheckResult result = new TypeCheckResult(); @@ -72,7 +72,7 @@ public class Program implements Node { } public void codeGen() throws Exception{ - try (JarOutputStream jos = new JarOutputStream(new FileOutputStream("output.jar"))) { + try { for (RefType oneClass : classes) { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, oneClass.name, null, "java/lang/Object", null); @@ -88,30 +88,9 @@ public class Program implements Node { e.printStackTrace(); } } + } catch (Exception e) { + throw new RuntimeException(e); } - - - /*// Write the bytecode to a .class file in the .jar file - JarEntry entry = new JarEntry(oneClass.name + ".class"); - jos.putNextEntry(entry); - jos.write(bytecode); - jos.closeEntry(); - } - } catch (IOException e) { - e.printStackTrace(); - }*/ - - /* - for(RefType oneClass : classes){ - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,oneClass.name, null, - "java/lang/Object", null); - oneClass.codeGen(cw); - - cw.visitEnd(); - byte[] bytecode = cw.toByteArray(); - } - */ } @Override diff --git a/src/main/java/abstractSyntaxTree/Statement/IfElseStatement.java b/src/main/java/abstractSyntaxTree/Statement/IfElseStatement.java index d380fae..b290dcf 100644 --- a/src/main/java/abstractSyntaxTree/Statement/IfElseStatement.java +++ b/src/main/java/abstractSyntaxTree/Statement/IfElseStatement.java @@ -54,7 +54,7 @@ public class IfElseStatement extends AbstractType implements IStatement{ } - + //TODO: There are 2 NOPs and one athrow on the stack between the if-Block and else-Block execution --> I have no idea why @Override public void codeGen(MethodVisitor mv, LinkedHashMap localVars, HashMap> typeContext, HashMap>> methodContext) throws Exception { @@ -67,7 +67,8 @@ public class IfElseStatement extends AbstractType implements IStatement{ mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); //Checks if the condition is false (0) ifStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is true, execute the ifBlock - mv.visitJumpInsn(Opcodes.GOTO, statementEnd); //Jump to the end of the if-else statement + + mv.visitJumpInsn(Opcodes.GOTO, statementEnd); //Jump to the end of the else statement mv.visitLabel(conditionFalse); elseStatement.codeGen(mv, blockLocalVars, typeContext, methodContext); //If the condition is false, execute the elseBlock diff --git a/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java b/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java index 9e986a4..4a3eef7 100644 --- a/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java +++ b/src/main/java/abstractSyntaxTree/Statement/LocalVarDecl.java @@ -55,7 +55,19 @@ public class LocalVarDecl extends AbstractType implements IStatement{ public void codeGen(MethodVisitor mv, LinkedHashMap localVars, HashMap> typeContext, HashMap>> methodContext) throws Exception { localVars.put(identifier, type); - int index = localVars.size() - 1; + int index = -1; + int counter = 0; + for (String key : localVars.keySet()){ + if (key.equals(identifier)){ + index = counter+1; // +1 because the first local variable is at index 1, 0 is used for "this" + break; + } + counter++; + } + + if (index == -1){ + throw new Exception("Variable " + identifier + " not found"); + } if (expression != null) { expression.codeGen(mv, localVars, typeContext,methodContext); @@ -72,11 +84,7 @@ public class LocalVarDecl extends AbstractType implements IStatement{ // Set a default value for the variable --> less problems switch (type) { - case "int": - mv.visitInsn(Opcodes.ICONST_1); - mv.visitVarInsn(Opcodes.ISTORE, index); - break; - case "boolean": + case "int", "boolean", "char": mv.visitInsn(Opcodes.ICONST_0); mv.visitVarInsn(Opcodes.ISTORE, index); break; diff --git a/src/main/java/abstractSyntaxTree/Statement/WhileStatement.java b/src/main/java/abstractSyntaxTree/Statement/WhileStatement.java index 47255ea..a81de81 100644 --- a/src/main/java/abstractSyntaxTree/Statement/WhileStatement.java +++ b/src/main/java/abstractSyntaxTree/Statement/WhileStatement.java @@ -55,7 +55,6 @@ public class WhileStatement extends AbstractType implements IStatement { mv.visitJumpInsn(Opcodes.IFEQ, conditionFalse); // Checks if the condition is false (0) statement.codeGen(mv, blockLocalVars, typeContext, methodContext); - //statement.codeGen(mv); //TODO: If the block ends with a return statement, we might have to pop it from the stack // So the next iteration starts with a clean stack mv.visitJumpInsn(Opcodes.GOTO, LoopStart); // Jump to the start of the while loop diff --git a/src/main/java/astGenerator/ASTGenerator.java b/src/main/java/astGenerator/ASTGenerator.java index 0b23d52..2127a6e 100644 --- a/src/main/java/astGenerator/ASTGenerator.java +++ b/src/main/java/astGenerator/ASTGenerator.java @@ -56,7 +56,12 @@ public class ASTGenerator extends DecafBaseVisitor { } public Node generateFieldDecl(DecafParser.LocalVarDeclContext ctx) { - return new FieldDecl(ctx.type().getText(), ctx.Identifier().getText()); + if (ctx.expression() != null) { + IExpression expression = (IExpression) visit(ctx.expression()); + return new FieldDecl(ctx.type().getText(), ctx.Identifier().getText(), expression); + } else { + return new FieldDecl(ctx.type().getText(), ctx.Identifier().getText(), null); + } } @Override @@ -198,7 +203,6 @@ public class ASTGenerator extends DecafBaseVisitor { return new AssignStatementExpression(ctx.Assign().getText(),(IExpression) left, (IExpression) right); } - // @Override public Node visitMethodCall(DecafParser.MethodCallContext ctx) { String methodName = ctx.Identifier().getText();