Merge remote-tracking branch 'origin/code-generator' into Tests

This commit is contained in:
Lucas 2024-07-02 23:45:33 +02:00
commit e862a7427b
10 changed files with 205 additions and 137 deletions

View File

@ -4,6 +4,7 @@ import ast.type.AccessModifierNode;
import ast.members.ConstructorNode; import ast.members.ConstructorNode;
import ast.members.MemberNode; import ast.members.MemberNode;
import ast.members.MethodNode; import ast.members.MethodNode;
import bytecode.visitor.ClassVisitor;
import semantic.SemanticVisitor; import semantic.SemanticVisitor;
import typechecker.TypeCheckResult; import typechecker.TypeCheckResult;
import visitor.Visitable; import visitor.Visitable;
@ -44,4 +45,9 @@ public class ClassNode implements ASTNode, Visitable {
return visitor.analyze(this); return visitor.analyze(this);
} }
@Override
public void accept(ClassVisitor classVisitor) {
classVisitor.visit(this);
}
} }

View File

@ -1,6 +1,5 @@
package ast.expressions.binaryexpressions; package ast.expressions.binaryexpressions;
import ast.type.type.*;
import bytecode.visitor.MethodVisitor; import bytecode.visitor.MethodVisitor;
import semantic.SemanticVisitor; import semantic.SemanticVisitor;
import typechecker.TypeCheckResult; import typechecker.TypeCheckResult;

View File

@ -1,4 +1,5 @@
package ast.literal; package ast.literal;
import ast.expressions.IExpressionNode; import ast.expressions.IExpressionNode;
import ast.type.type.ITypeNode; import ast.type.type.ITypeNode;
import semantic.SemanticVisitor; import semantic.SemanticVisitor;

View File

@ -20,10 +20,4 @@ public class DecrementNode implements IStatementExpressionNode {
public TypeCheckResult accept(SemanticVisitor visitor) { public TypeCheckResult accept(SemanticVisitor visitor) {
return visitor.analyze(this); return visitor.analyze(this);
} }
@Override
public void accept(MethodVisitor methodVisitor) {
methodVisitor.visit(this);
}
} }

View File

@ -19,10 +19,4 @@ public class IncrementNode implements IStatementExpressionNode {
public TypeCheckResult accept(SemanticVisitor visitor) { public TypeCheckResult accept(SemanticVisitor visitor) {
return visitor.analyze(this); return visitor.analyze(this);
} }
@Override
public void accept(MethodVisitor methodVisitor) {
methodVisitor.visit(this);
}
} }

View File

@ -18,49 +18,60 @@ public class ByteCodeGenerator implements ProgramVisitor {
private JarOutputStream jarOutputStream; private JarOutputStream jarOutputStream;
private ByteArrayOutputStream byteArrayOutputStream; private ByteArrayOutputStream byteArrayOutputStream;
private String outputDirectory; private String outputDirectory;
private boolean generateJar;
private boolean generateClassFiles;
public ByteCodeGenerator(String outputDirectory) { public ByteCodeGenerator(String outputDirectory, boolean generateJar, boolean generateClassFiles) {
this.outputDirectory = outputDirectory; this.outputDirectory = outputDirectory;
this.generateJar = generateJar;
this.generateClassFiles = generateClassFiles;
} }
@Override @Override
public void visit(ProgramNode programNode) { public void visit(ProgramNode programNode) {
byteArrayOutputStream = new ByteArrayOutputStream(); if(generateJar) {
try { byteArrayOutputStream = new ByteArrayOutputStream();
Manifest manifest = new Manifest(); try {
manifest.getMainAttributes().putValue("Manifest-Version", "1.0"); Manifest manifest = new Manifest();
boolean foundMainClass = false; manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
for (ClassNode classNode : programNode.classes) { boolean foundMainClass = false;
if (foundMainClass) { for (ClassNode classNode : programNode.classes) {
break; if (foundMainClass) {
}
for (MemberNode memberNode : classNode.members) {
if (memberNode instanceof MainMethodNode) {
manifest.getMainAttributes().putValue("Main-Class", classNode.identifier);
foundMainClass = true;
break; break;
} }
for (MemberNode memberNode : classNode.members) {
if (memberNode instanceof MainMethodNode) {
manifest.getMainAttributes().putValue("Main-Class", classNode.identifier);
foundMainClass = true;
break;
}
}
} }
jarOutputStream = new JarOutputStream(byteArrayOutputStream, manifest);
} catch (IOException e) {
throw new RuntimeException(e);
} }
for (ClassNode classDeclarationNode : programNode.classes) {
ClassCodeGen classCodeGen = new ClassCodeGen(jarOutputStream, outputDirectory, generateJar, generateClassFiles);
classDeclarationNode.accept(classCodeGen);
}
jarOutputStream = new JarOutputStream(byteArrayOutputStream, manifest); try {
} catch (IOException e) { jarOutputStream.close();
throw new RuntimeException(e); } catch (IOException e) {
e.printStackTrace();
}
saveJarFile(byteArrayOutputStream.toByteArray(), "output.jar");
} else {
for (ClassNode classDeclarationNode : programNode.classes) {
ClassCodeGen classCodeGen = new ClassCodeGen(jarOutputStream, outputDirectory, generateJar, generateClassFiles);
classDeclarationNode.accept(classCodeGen);
}
} }
for (ClassNode classDeclarationNode : programNode.classes) {
ClassCodeGen classCodeGen = new ClassCodeGen(jarOutputStream, outputDirectory);
classDeclarationNode.accept(classCodeGen);
}
try {
jarOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
saveJarFile(byteArrayOutputStream.toByteArray(), "output.jar");
} }
private void saveJarFile(byte[] jarBytes, String jarFileName) { private void saveJarFile(byte[] jarBytes, String jarFileName) {

View File

@ -23,11 +23,15 @@ public class ClassCodeGen implements ClassVisitor {
private ClassWriter classWriter; private ClassWriter classWriter;
private JarOutputStream jarOutputStream; private JarOutputStream jarOutputStream;
private String outputDirectory; private String outputDirectory;
private boolean generateJar;
private boolean generateClassFiles;
public ClassCodeGen(JarOutputStream jarOutputStream, String outputDirectory) { public ClassCodeGen(JarOutputStream jarOutputStream, String outputDirectory, boolean generateJar, boolean generateClassFiles) {
mapper = new Mapper(); mapper = new Mapper();
this.jarOutputStream = jarOutputStream; this.jarOutputStream = jarOutputStream;
this.outputDirectory = outputDirectory; this.outputDirectory = outputDirectory;
this.generateJar = generateJar;
this.generateClassFiles = generateClassFiles;
} }
@Override @Override
@ -45,9 +49,12 @@ public class ClassCodeGen implements ClassVisitor {
} }
} }
classWriter.visitEnd(); if (generateJar) {
printIntoClassFile(classWriter.toByteArray(), classNode.identifier, outputDirectory); writeToJar(classWriter.toByteArray(), classNode.identifier);
writeToJar(classWriter.toByteArray(), classNode.identifier); }
if (generateClassFiles) {
printIntoClassFile(classWriter.toByteArray(), classNode.identifier, outputDirectory);
}
classWriter.visitEnd(); classWriter.visitEnd();
} }

View File

@ -27,18 +27,18 @@ public class Mapper {
public String generateMethodDescriptor(BaseType type, List<ParameterNode> parameters) { public String generateMethodDescriptor(BaseType type, List<ParameterNode> parameters) {
String descriptor = "("; String descriptor = "(";
for (ParameterNode parameterNode : parameters) { for (ParameterNode parameterNode : parameters) {
descriptor += getTypeChar((BaseType) parameterNode.type); if(parameterNode.type instanceof BaseType) {
descriptor += getTypeChar((BaseType) parameterNode.type);
} else {
ReferenceType referenceType = (ReferenceType) parameterNode.type;
descriptor += "L" + referenceType.getIdentifier() + ";";
}
} }
descriptor += ")"; descriptor += ")";
descriptor += getTypeChar(type); descriptor += getTypeChar(type);
return descriptor; return descriptor;
} }
public String generateMethodDescriptor(ReferenceType type, List<ParameterNode> parameters) {
String descriptor = "()V";
return descriptor;
}
public String getTypeChar(BaseType type) { public String getTypeChar(BaseType type) {
String typeChar = ""; String typeChar = "";
switch (type.getTypeEnum()) { switch (type.getTypeEnum()) {

View File

@ -1,5 +1,6 @@
package bytecode; package bytecode;
import ast.expressions.IExpressionNode;
import ast.expressions.binaryexpressions.*; import ast.expressions.binaryexpressions.*;
import ast.expressions.unaryexpressions.MemberAccessNode; import ast.expressions.unaryexpressions.MemberAccessNode;
import ast.expressions.unaryexpressions.NotNode; import ast.expressions.unaryexpressions.NotNode;
@ -10,6 +11,7 @@ import ast.members.MethodNode;
import ast.parameters.ParameterNode; import ast.parameters.ParameterNode;
import ast.statementexpressions.AssignNode; import ast.statementexpressions.AssignNode;
import ast.statementexpressions.NewDeclarationNode; import ast.statementexpressions.NewDeclarationNode;
import ast.statementexpressions.crementexpressions.CrementType;
import ast.statementexpressions.crementexpressions.DecrementNode; import ast.statementexpressions.crementexpressions.DecrementNode;
import ast.statementexpressions.crementexpressions.IncrementNode; import ast.statementexpressions.crementexpressions.IncrementNode;
import ast.statementexpressions.methodcallstatementnexpressions.ChainedMethodNode; import ast.statementexpressions.methodcallstatementnexpressions.ChainedMethodNode;
@ -18,6 +20,7 @@ import ast.statementexpressions.methodcallstatementnexpressions.TargetNode;
import ast.statements.*; import ast.statements.*;
import ast.type.ValueNode; import ast.type.ValueNode;
import ast.type.type.BaseType; import ast.type.type.BaseType;
import ast.type.type.ReferenceType;
import ast.type.type.TypeEnum; import ast.type.type.TypeEnum;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
@ -68,7 +71,9 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
// Visit all statements // Visit all statements
for (IStatementNode statementNode : constructorNode.block.statements) { for (IStatementNode statementNode : constructorNode.block.statements) {
statementNode.accept(this); if (statementNode != null) {
statementNode.accept(this);
}
} }
methodVisitor.visitMaxs(0, 0); methodVisitor.visitMaxs(0, 0);
@ -130,32 +135,44 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
@Override @Override
public void visit(CalculationNode calculationNode) { public void visit(CalculationNode calculationNode) {
calculationNode.dotExpression.accept(this); if (calculationNode.dotExpression != null) {
calculationNode.calculationExpression.accept(this); calculationNode.dotExpression.accept(this);
switch (calculationNode.operator) { }
case PLUS: if (calculationNode.calculationExpression != null) {
methodVisitor.visitInsn(IADD); calculationNode.calculationExpression.accept(this);
break; }
case MINUS: if (calculationNode.operator != null) {
methodVisitor.visitInsn(ISUB); switch (calculationNode.operator) {
break; case PLUS:
methodVisitor.visitInsn(IADD);
break;
case MINUS:
methodVisitor.visitInsn(ISUB);
break;
}
} }
} }
@Override @Override
public void visit(DotNode dotNode) { public void visit(DotNode dotNode) {
dotNode.dotExpression.accept(this); if (dotNode.dotExpression != null) {
dotNode.dotSubstractionExpression.accept(this); dotNode.dotExpression.accept(this);
switch (dotNode.operator) { }
case DIV: if (dotNode.dotSubstractionExpression != null) {
methodVisitor.visitInsn(IDIV); dotNode.dotSubstractionExpression.accept(this);
break; }
case MULT: if (dotNode.operator != null) {
methodVisitor.visitInsn(IMUL); switch (dotNode.operator) {
break; case DIV:
case MOD: methodVisitor.visitInsn(IDIV);
methodVisitor.visitInsn(IREM); break;
break; case MULT:
methodVisitor.visitInsn(IMUL);
break;
case MOD:
methodVisitor.visitInsn(IREM);
break;
}
} }
} }
@ -178,6 +195,7 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
public void visit(NonCalculationNode nonCalculationNode) { public void visit(NonCalculationNode nonCalculationNode) {
Label labelFalse = new Label(); Label labelFalse = new Label();
Label labelTrue = new Label(); Label labelTrue = new Label();
// TODO: Null check
switch (nonCalculationNode.operator) { switch (nonCalculationNode.operator) {
case AND: case AND:
nonCalculationNode.unaryExpression.accept(this); nonCalculationNode.unaryExpression.accept(this);
@ -249,7 +267,7 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
@Override @Override
public void visit(MemberAccessNode memberAccessNode) { public void visit(MemberAccessNode memberAccessNode) {
if (memberAccessNode.thisExpr) { if (memberAccessNode.thisExpr) {
//methodVisitor.visitFieldInsn(PUTFIELD); // methodVisitor.visitFieldInsn(PUTFIELD, memberAccessNode.identifiers.get(0), memberAccessNode.identifiers.get(1), );
} }
} }
@ -275,7 +293,21 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
@Override @Override
public void visit(UnaryNode unaryNode) { public void visit(UnaryNode unaryNode) {
if (unaryNode.thisExp != null) {
methodVisitor.visitVarInsn(ALOAD, 0); // this
} else if (unaryNode.identifier != null) {
methodVisitor.visitVarInsn(ILOAD, localVaribales.indexOf(unaryNode.identifier));
} else if (unaryNode.memberAccess != null) {
unaryNode.memberAccess.accept(this);
} else if (unaryNode.value != null) {
unaryNode.value.accept(this);
} else if (unaryNode.notExpression != null) {
unaryNode.notExpression.accept(this);
} else if (unaryNode.statement != null) {
unaryNode.statement.accept(this);
} else if (unaryNode.expression != null) {
unaryNode.expression.accept(this);
}
} }
@ -286,7 +318,7 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
Label elseLabel = new Label(); Label elseLabel = new Label();
Label[] elseIfLabels = new Label[ifElseNode.elseIfStatements.size()]; Label[] elseIfLabels = new Label[ifElseNode.elseIfStatements.size()];
for(int i = 0; i < ifElseNode.elseIfStatements.size(); i++) { for (int i = 0; i < ifElseNode.elseIfStatements.size(); i++) {
elseIfLabels[i] = new Label(); elseIfLabels[i] = new Label();
} }
@ -303,12 +335,12 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
Label endLabel = new Label(); Label endLabel = new Label();
methodVisitor.visitJumpInsn(GOTO, endLabel); methodVisitor.visitJumpInsn(GOTO, endLabel);
for(int i = 0; i < ifElseNode.elseIfStatements.size(); i++) { for (int i = 0; i < ifElseNode.elseIfStatements.size(); i++) {
methodVisitor.visitLabel(elseIfLabels[i]); methodVisitor.visitLabel(elseIfLabels[i]);
ifElseNode.elseIfStatements.get(i).expression.accept(this); ifElseNode.elseIfStatements.get(i).expression.accept(this);
if(i + 1 < elseIfLabels.length) { if (i + 1 < elseIfLabels.length) {
// at least one more else if // at least one more else if
methodVisitor.visitJumpInsn(IFEQ, elseIfLabels[i+1]); methodVisitor.visitJumpInsn(IFEQ, elseIfLabels[i + 1]);
} else { } else {
methodVisitor.visitJumpInsn(IFEQ, elseLabel); methodVisitor.visitJumpInsn(IFEQ, elseLabel);
} }
@ -316,7 +348,7 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
methodVisitor.visitJumpInsn(GOTO, endLabel); methodVisitor.visitJumpInsn(GOTO, endLabel);
} }
if(ifElseNode.elseStatement != null) { if (ifElseNode.elseStatement != null) {
methodVisitor.visitLabel(elseLabel); methodVisitor.visitLabel(elseLabel);
ifElseNode.elseStatement.block.accept(this); ifElseNode.elseStatement.block.accept(this);
} }
@ -326,29 +358,88 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
@Override @Override
public void visit(LocalVariableDeclarationNode localVariableDeclarationNode) { public void visit(LocalVariableDeclarationNode localVariableDeclarationNode) {
// Process expression if (localVariableDeclarationNode.expression != null) {
localVariableDeclarationNode.expression.accept(this); // Process expression
// Store result of expression in variable localVariableDeclarationNode.expression.accept(this);
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(localVariableDeclarationNode.identifier)); // Store result of expression in variable
if (localVaribales.contains(localVariableDeclarationNode.identifier)) {
if (localVariableDeclarationNode.type instanceof BaseType) {
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(localVariableDeclarationNode.identifier));
} else if (localVariableDeclarationNode.type instanceof ReferenceType) {
methodVisitor.visitVarInsn(ASTORE, localVaribales.indexOf(localVariableDeclarationNode.identifier));
}
} else {
localVaribales.add(localVariableDeclarationNode.identifier);
if (localVariableDeclarationNode.type instanceof BaseType) {
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(localVariableDeclarationNode.identifier));
} else if (localVariableDeclarationNode.type instanceof ReferenceType) {
methodVisitor.visitVarInsn(ASTORE, localVaribales.indexOf(localVariableDeclarationNode.identifier));
}
}
} else {
if (!localVaribales.contains(localVariableDeclarationNode.identifier)) {
localVaribales.add(localVariableDeclarationNode.identifier);
}
}
} }
@Override @Override
public void visit(AssignNode assignNode) { public void visit(AssignNode assignNode) {
// Process expression // Process expression
assignNode.expression.accept(this); if (assignNode.expression instanceof IncrementNode) {
IncrementNode incrementNode = (IncrementNode) assignNode.expression;
if (incrementNode.crementType.equals(CrementType.PREFIX)) { // ++i
methodVisitor.visitIincInsn(localVaribales.indexOf(incrementNode.assignableExpression.identifier), 1);
assign(assignNode);
} else if (incrementNode.crementType.equals(CrementType.SUFFIX)) { // Suffix: i++
assign(assignNode);
methodVisitor.visitIincInsn(localVaribales.indexOf(incrementNode.assignableExpression.identifier), 1);
}
} else if (assignNode.expression instanceof DecrementNode) {
DecrementNode decrementNode = (DecrementNode) assignNode.expression;
if (decrementNode.crementType.equals(CrementType.PREFIX)) {
methodVisitor.visitIincInsn(localVaribales.indexOf(decrementNode.assignableExpression.identifier), -1);
assign(assignNode);
} else if (decrementNode.crementType.equals(CrementType.SUFFIX)) {
assign(assignNode);
methodVisitor.visitIincInsn(localVaribales.indexOf(decrementNode.assignableExpression.identifier), 1);
}
} else {
assignNode.expression.accept(this);
}
}
private void assign(AssignNode assignNode) {
// Store result of expression in variable // Store result of expression in variable
if (assignNode.assignable.memberAccess.thisExpr) { if (assignNode.assignable.memberAccess.thisExpr) {
// Global var // Global var
// /methodVisitor.visitFieldInsn(PUTFIELD, identifierExpressionNode.name, identifierExpressionNode1.name, mapper.getTypeChar(((BaseTypeNode) type).enumType)); methodVisitor.visitVarInsn(ALOAD, 0);
if (assignNode.expression instanceof BaseType) {
//methodVisitor.visitFieldInsn(PUTFIELD, class name, var identifier, mapper.getTypeChar(((BaseTypeNode) type).enumType));
} else if (assignNode.expression instanceof ReferenceType) {
//methodVisitor.visitFieldInsn(PUTFIELD, class name, var identifier, "L"class name object +";");
}
} else { } else {
// Local var // Local var
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(assignNode.assignable.identifier)); if (assignNode.expression instanceof BaseType) {
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(assignNode.assignable.identifier));
} else if (assignNode.expression instanceof ReferenceType) {
methodVisitor.visitVarInsn(ASTORE, localVaribales.indexOf(assignNode.assignable.identifier));
}
} }
} }
@Override @Override
public void visit(NewDeclarationNode newDeclarationNode) { public void visit(NewDeclarationNode newDeclarationNode) {
methodVisitor.visitTypeInsn(NEW, newDeclarationNode.identifier);
methodVisitor.visitInsn(DUP);
for (IExpressionNode expressionNode : newDeclarationNode.expressions) {
expressionNode.accept(this);
}
// TODO
//methodVisitor.visitMethodInsn(INVOKESPECIAL, class name, "<init>", mapper.generateMethodDescriptor(), false);
// TODO: kann ein Field auch definiert werden? Abfrage ob local var oder field
localVaribales.add(newDeclarationNode.identifier);
} }
@Override @Override
@ -395,7 +486,11 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
// Process expression // Process expression
returnNode.expression.accept(this); returnNode.expression.accept(this);
// Return result of expression // Return result of expression
methodVisitor.visitInsn(IRETURN); if (returnNode.expression.getType() instanceof BaseType) {
methodVisitor.visitInsn(IRETURN);
} else if (returnNode.expression.getType() instanceof ReferenceType) {
methodVisitor.visitInsn(ARETURN);
}
} }
} }
@ -410,55 +505,19 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor {
whileNode.expression.accept(this); whileNode.expression.accept(this);
methodVisitor.visitJumpInsn(IFEQ, endOfLoopLabel); // if condition is false, jump out of loop methodVisitor.visitJumpInsn(IFEQ, endOfLoopLabel); // if condition is false, jump out of loop
// TODO: Unterscheidung bei increment/decrement der for Schleife
if (whileNode.block.statements.size() == 2) { // For loop
whileNode.block.statements.get(0).accept(this);
} else {
whileNode.block.statements.get(0).accept(this);
}
whileNode.block.accept(this); whileNode.block.accept(this);
methodVisitor.visitJumpInsn(GOTO, loopLabel); methodVisitor.visitJumpInsn(GOTO, loopLabel);
methodVisitor.visitLabel(endOfLoopLabel); methodVisitor.visitLabel(endOfLoopLabel);
} }
@Override
public void visit(DecrementNode decrementNode) {
switch (decrementNode.crementType) {
case PREFIX: // --i
if (decrementNode.assignableExpression.memberAccess == null) { // local Var
methodVisitor.visitIincInsn(localVaribales.indexOf(decrementNode.assignableExpression.identifier), -1);
} else { // Field or var from other object
}
break;
case SUFFIX: // i--
if (decrementNode.assignableExpression.memberAccess == null) { // local Var
methodVisitor.visitIincInsn(localVaribales.indexOf(decrementNode.assignableExpression.identifier), -1);
} else { // Field or var from other object
}
break;
}
}
@Override
public void visit(IncrementNode incrementNode) {
switch (incrementNode.crementType) {
case PREFIX: // ++i
if (incrementNode.assignableExpression.memberAccess == null) { // local Var
methodVisitor.visitIincInsn(localVaribales.indexOf(incrementNode.assignableExpression.identifier), 1);
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(incrementNode.assignableExpression.identifier));
} else { // Field or var from other object
}
break;
case SUFFIX: // i++
if (incrementNode.assignableExpression.memberAccess == null) { // local Var
methodVisitor.visitVarInsn(ISTORE, localVaribales.indexOf(incrementNode.assignableExpression.identifier));
methodVisitor.visitIincInsn(localVaribales.indexOf(incrementNode.assignableExpression.identifier), 1);
} else { // Field or var from other object
}
break;
}
}
@Override @Override
public void visit(ChainedMethodNode chainedMethodNode) { public void visit(ChainedMethodNode chainedMethodNode) {

View File

@ -44,9 +44,6 @@ public interface MethodVisitor {
void visit(WhileNode whileNode); void visit(WhileNode whileNode);
// statement expression // statement expression
void visit(DecrementNode decrementNode);
void visit(IncrementNode incrementNode);
void visit(ChainedMethodNode chainedMethodNode); void visit(ChainedMethodNode chainedMethodNode);
void visit(MethodCallNode methodCallNode); void visit(MethodCallNode methodCallNode);
void visit(TargetNode targetNode); void visit(TargetNode targetNode);