forked from JavaTX/JavaCompilerCore
Mostly finish up populating generics
This commit is contained in:
parent
f00ee2598f
commit
7e259e2597
@ -1,6 +1,5 @@
|
|||||||
package de.dhbwstuttgart.target.generate;
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.*;
|
import de.dhbwstuttgart.syntaxtree.*;
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.*;
|
import de.dhbwstuttgart.syntaxtree.statement.*;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.*;
|
import de.dhbwstuttgart.syntaxtree.type.*;
|
||||||
@ -11,7 +10,6 @@ import de.dhbwstuttgart.target.tree.type.*;
|
|||||||
import de.dhbwstuttgart.typeinference.result.*;
|
import de.dhbwstuttgart.typeinference.result.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class ASTToTargetAST {
|
public class ASTToTargetAST {
|
||||||
|
|
||||||
@ -19,10 +17,11 @@ public class ASTToTargetAST {
|
|||||||
protected Sigma sigma;
|
protected Sigma sigma;
|
||||||
|
|
||||||
private class Sigma {
|
private class Sigma {
|
||||||
HashMap<Method, Set<TargetGeneric>> computedGenerics = new HashMap<>();
|
Map<Method, HashSet<TargetGeneric>> computedGenericsOfMethods = new HashMap<>();
|
||||||
|
Map<ClassOrInterface, HashSet<TargetGeneric>> computedGenericsOfClasses = new HashMap<>();
|
||||||
|
|
||||||
Set<PairTPHsmallerTPH> simplifiedConstraints = new HashSet<>();
|
HashSet<PairTPHsmallerTPH> simplifiedConstraints = new HashSet<>();
|
||||||
Map<TypePlaceholder, TargetType> result = new HashMap<>();
|
Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
|
||||||
Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
|
Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
|
||||||
|
|
||||||
Sigma(ResultSet constraints) {
|
Sigma(ResultSet constraints) {
|
||||||
@ -62,11 +61,9 @@ public class ASTToTargetAST {
|
|||||||
right = unified.get(right).get(0);
|
right = unified.get(right).get(0);
|
||||||
|
|
||||||
simplifiedConstraints.add(new PairTPHsmallerTPH(left, right));
|
simplifiedConstraints.add(new PairTPHsmallerTPH(left, right));
|
||||||
result.put(right, new TargetGenericType(right.getName()));
|
|
||||||
result.put(left, new TargetGenericType(left.getName()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println(simplifiedConstraints);
|
System.out.println("Simplified constraints: " + simplifiedConstraints);
|
||||||
|
|
||||||
for (var equality : equalitySet) {
|
for (var equality : equalitySet) {
|
||||||
var first = equality.get(0);
|
var first = equality.get(0);
|
||||||
@ -76,155 +73,190 @@ public class ASTToTargetAST {
|
|||||||
|
|
||||||
for (var constraint : constraints.results) {
|
for (var constraint : constraints.results) {
|
||||||
if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
|
if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
|
||||||
result.put(p.left, convert(p.right));
|
concreteTypes.put(this.equality.getOrDefault(p.left, p.left), p.right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type, Set<TypePlaceholder> typeVariables) {
|
||||||
|
if (type instanceof TypePlaceholder tph) {
|
||||||
|
tph = equality.getOrDefault(tph, tph);
|
||||||
|
if (concreteTypes.containsKey(tph)) {
|
||||||
|
findTypeVariables(concreteTypes.get(tph), typeVariables);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
typeVariables.add(tph);
|
||||||
|
} else if (type instanceof RefType refType) {
|
||||||
|
for (var t : refType.getParaList())
|
||||||
|
findTypeVariables(t, typeVariables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasBound(String name, Set<TargetGeneric> generics) {
|
||||||
|
return generics.stream().anyMatch(generic -> generic.name().equals(name));
|
||||||
|
}
|
||||||
|
|
||||||
// Family of generated Generics
|
// Family of generated Generics
|
||||||
Set<TargetGeneric> generics(ClassOrInterface owner, Method method) {
|
HashSet<TargetGeneric> generics(ClassOrInterface owner, Method method) {
|
||||||
if (computedGenerics.containsKey(method))
|
if (computedGenericsOfMethods.containsKey(method))
|
||||||
return computedGenerics.get(method);
|
return computedGenericsOfMethods.get(method);
|
||||||
|
|
||||||
Set<TargetGeneric> result = new HashSet<>();
|
HashSet<TargetGeneric> result = new HashSet<>();
|
||||||
computedGenerics.put(method, result);
|
computedGenericsOfMethods.put(method, result);
|
||||||
|
|
||||||
method.block.accept(new AbstractStatementVisitor() {
|
var genericsOfClass = generics(owner);
|
||||||
// These two methods do the actual work
|
var simplifiedConstraints = (HashSet<PairTPHsmallerTPH>) this.simplifiedConstraints.clone();
|
||||||
|
|
||||||
|
HashSet<TypePlaceholder> typeVariables = new HashSet<>();
|
||||||
|
HashSet<TypePlaceholder> typeVariablesOfFields = new HashSet<>();
|
||||||
|
|
||||||
|
for (var field : owner.getFieldDecl()) {
|
||||||
|
findTypeVariables(field.getType(), typeVariablesOfFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
findTypeVariables(method.getReturnType(), typeVariables);
|
||||||
|
for (var arg : method.getParameterList().getFormalparalist()) {
|
||||||
|
findTypeVariables(arg.getType(), typeVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.block.accept(new TracingStatementVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(LocalVarDecl localVarDecl) {
|
public void visit(LocalVarDecl localVarDecl) {
|
||||||
// TODO
|
findTypeVariables(localVarDecl.getType(), typeVariables);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
method.block.accept(new TracingStatementVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodCall methodCall) {
|
public void visit(MethodCall methodCall) {
|
||||||
|
super.visit(methodCall);
|
||||||
|
|
||||||
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
|
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
|
||||||
if (expressionReceiver.expr instanceof This) {
|
if (expressionReceiver.expr instanceof This) {
|
||||||
// TODO This is going to fail spectacularly for overloaded methods
|
// TODO This is going to fail spectacularly for overloaded methods
|
||||||
var optMethod = owner.getMethods().stream().filter(m -> m.name.equals(methodCall.name)).findFirst();
|
var optMethod = owner.getMethods().stream().filter(m -> m.name.equals(methodCall.name)).findFirst();
|
||||||
assert optMethod.isPresent();
|
assert optMethod.isPresent();
|
||||||
var method = optMethod.get();
|
var method = optMethod.get();
|
||||||
Set<TargetGeneric> generics = generics(owner, method);
|
var generics = generics(owner, method);
|
||||||
|
HashSet<TargetGeneric> all = new HashSet<>(generics);
|
||||||
|
|
||||||
|
// Reflexive and Transitive closure
|
||||||
|
HashSet<TargetGeneric> toAdd = new HashSet<>();
|
||||||
|
do {
|
||||||
|
toAdd.clear();
|
||||||
|
for (var g1 : all) {
|
||||||
|
for (var g2 : all) {
|
||||||
|
if (g1.bound() instanceof TargetGenericType type) {
|
||||||
|
if (g2.name().equals(type.name()) && generics.stream().anyMatch(generic -> generic.name().equals(type.name())))
|
||||||
|
toAdd.add(new TargetGeneric(g1.name(), g2.bound()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all.addAll(toAdd);
|
||||||
|
} while (toAdd.size() > 0);
|
||||||
|
for (var generic : all) {
|
||||||
|
toAdd.add(new TargetGeneric(generic.name(), new TargetGenericType(generic.name())));
|
||||||
|
}
|
||||||
|
all.addAll(toAdd);
|
||||||
|
|
||||||
|
HashSet<PairTPHsmallerTPH> newPairs = new HashSet<>();
|
||||||
|
// Loop from hell
|
||||||
|
outer: for (var tph : typeVariables) {
|
||||||
|
for (var generic : all) {
|
||||||
|
if (!(generic.bound() instanceof TargetGenericType type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (var pair : simplifiedConstraints) {
|
||||||
|
if (!(pair.left.equals(tph) && pair.right.getName().equals(generic.name())))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (var tph2 : typeVariables) {
|
||||||
|
for (var pair2 : simplifiedConstraints) {
|
||||||
|
if (!(pair2.right.equals(tph2) && pair2.left.getName().equals(type.name())))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
newPairs.add(new PairTPHsmallerTPH(tph, tph2));
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simplifiedConstraints.addAll(newPairs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything down here is just for walking the AST
|
|
||||||
@Override
|
|
||||||
public void visit(ArgumentList argumentList) {
|
|
||||||
argumentList.getArguments().forEach(expr -> expr.accept(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(LambdaExpression lambdaExpression) {
|
|
||||||
lambdaExpression.methodBody.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Assign assign) {
|
|
||||||
assign.rightSide.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(BinaryExpr binary) {
|
|
||||||
binary.lexpr.accept(this);
|
|
||||||
binary.rexpr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Block block) {
|
|
||||||
for (var expr : block.statements)
|
|
||||||
expr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ForStmt forStmt) {
|
|
||||||
forStmt.body_Loop_block.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(IfStmt ifStmt) {
|
|
||||||
ifStmt.then_block.accept(this);
|
|
||||||
ifStmt.else_block.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NewClass newClass) {
|
|
||||||
newClass.arglist.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NewArray newArray) {
|
|
||||||
newArray.expr.forEach(expr -> expr.accept(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Return aReturn) {
|
|
||||||
aReturn.retexpr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(WhileStmt whileStmt) {
|
|
||||||
whileStmt.loopBlock.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(DoStmt whileStmt) {
|
|
||||||
whileStmt.loopBlock.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(UnaryExpr unaryExpr) {
|
|
||||||
unaryExpr.expr.accept(this);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
// Type variables with bounds that are also type variables of the method or type variables of fields
|
||||||
}
|
for (var typeVariable : (HashSet<TypePlaceholder>)typeVariables.clone()) {
|
||||||
|
for (var pair : simplifiedConstraints) {
|
||||||
void findAllBounds(String name, Set<TargetGeneric> generics) {
|
if (pair.left.equals(typeVariable) && (typeVariables.contains(pair.right) || typeVariablesOfFields.contains(pair.right))) {
|
||||||
for (var rsp : simplifiedConstraints) {
|
result.add(new TargetGeneric(pair.left.getName(), convert(pair.right)));
|
||||||
var left = equality.getOrDefault(rsp.left, rsp.left);
|
typeVariables.add(pair.right);
|
||||||
var right = equality.getOrDefault(rsp.right, rsp.right);
|
|
||||||
if (left.getName().equals(name)) {
|
|
||||||
generics.add(new TargetGeneric(name, new TargetGenericType(right.getName())));
|
|
||||||
findAllBounds(right.getName(), generics);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
generics.add(new TargetGeneric(name, TargetType.Object));
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<TargetGeneric> findAllBounds(TargetGenericType type) {
|
|
||||||
Set<TargetGeneric> result = new HashSet<>();
|
|
||||||
findAllBounds(type.name(), result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<TargetGeneric> generics(ClassOrInterface classOrInterface) {
|
|
||||||
Set<TargetGeneric> result = new HashSet<>();
|
|
||||||
for (var field : classOrInterface.getFieldDecl()) {
|
|
||||||
if (field.getType() instanceof TypePlaceholder tph) {
|
|
||||||
var tpe = get(tph);
|
|
||||||
switch (tpe) {
|
|
||||||
case TargetSpecializedType specializedType:
|
|
||||||
specializedType.params().stream().flatMap(targetType -> {
|
|
||||||
if (targetType instanceof TargetSpecializedType spc)
|
|
||||||
return Stream.concat(Stream.of(targetType), spc.params().stream());
|
|
||||||
return Stream.of(targetType);
|
|
||||||
}).forEach(param -> {
|
|
||||||
if (param instanceof TargetGenericType genericType) {
|
|
||||||
result.addAll(findAllBounds(genericType));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case TargetGenericType genericType:
|
|
||||||
result.addAll(findAllBounds(genericType));
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All unbounded type variables
|
||||||
|
outer: for (var typeVariable : typeVariables) {
|
||||||
|
for (var pair : simplifiedConstraints) {
|
||||||
|
if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right))
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
if (!hasBound(typeVariable.getName(), genericsOfClass))
|
||||||
|
result.add(new TargetGeneric(typeVariable.getName(), TargetType.Object));
|
||||||
|
}
|
||||||
|
|
||||||
|
// All unbounded bounds
|
||||||
|
outer: for (var pair : simplifiedConstraints) {
|
||||||
|
for (var pair2 : simplifiedConstraints) {
|
||||||
|
if (pair.right.equals(pair2.left))
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
if (!hasBound(pair.right.getName(), genericsOfClass) && typeVariables.contains(pair.right))
|
||||||
|
result.add(new TargetGeneric(pair.right.getName(), TargetType.Object));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(method.name + ": " + result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set<TargetGeneric> generics) {
|
||||||
|
if (type instanceof TypePlaceholder tph) {
|
||||||
|
tph = equality.getOrDefault(tph, tph);
|
||||||
|
|
||||||
|
var concreteType = concreteTypes.get(tph);
|
||||||
|
if (concreteType != null) {
|
||||||
|
findAllBounds(concreteType, generics);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var rsp : simplifiedConstraints) {
|
||||||
|
var left = equality.getOrDefault(rsp.left, rsp.left);
|
||||||
|
var right = equality.getOrDefault(rsp.right, rsp.right);
|
||||||
|
if (left.equals(tph)) {
|
||||||
|
generics.add(new TargetGeneric(tph.getName(), new TargetGenericType(right.getName())));
|
||||||
|
findAllBounds(right, generics);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generics.add(new TargetGeneric(tph.getName(), TargetType.Object));
|
||||||
|
} else if (type instanceof RefType refType) {
|
||||||
|
refType.getParaList().forEach(t -> findAllBounds(t, generics));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<TargetGeneric> generics(ClassOrInterface classOrInterface) {
|
||||||
|
if (computedGenericsOfClasses.containsKey(classOrInterface))
|
||||||
|
return computedGenericsOfClasses.get(classOrInterface);
|
||||||
|
|
||||||
|
HashSet<TargetGeneric> result = new HashSet<>();
|
||||||
|
for (var field : classOrInterface.getFieldDecl()) {
|
||||||
|
findAllBounds(field.getType(), result);
|
||||||
|
}
|
||||||
|
computedGenericsOfClasses.put(classOrInterface, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +264,9 @@ public class ASTToTargetAST {
|
|||||||
if (equality.containsKey(tph)) {
|
if (equality.containsKey(tph)) {
|
||||||
return get(equality.get(tph));
|
return get(equality.get(tph));
|
||||||
}
|
}
|
||||||
return result.get(tph);
|
var type = concreteTypes.get(tph);
|
||||||
|
if (type == null) return new TargetGenericType(tph.getName());
|
||||||
|
return convert(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,30 +4,41 @@ import de.dhbwstuttgart.parser.SyntaxTreeGenerator.AssignToLocal;
|
|||||||
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
|
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.*;
|
import de.dhbwstuttgart.syntaxtree.statement.*;
|
||||||
|
|
||||||
public abstract class AbstractStatementVisitor implements StatementVisitor {
|
|
||||||
|
// This visitor walks the entire tree, individual methods may be overridden
|
||||||
|
public abstract class TracingStatementVisitor implements StatementVisitor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MethodCall methodCall) {
|
||||||
|
methodCall.receiver.accept(this);
|
||||||
|
methodCall.getArgumentList().accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ArgumentList argumentList) {
|
public void visit(ArgumentList argumentList) {
|
||||||
|
argumentList.getArguments().forEach(expr -> expr.accept(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(LambdaExpression lambdaExpression) {
|
public void visit(LambdaExpression lambdaExpression) {
|
||||||
|
lambdaExpression.methodBody.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Assign assign) {
|
public void visit(Assign assign) {
|
||||||
|
assign.rightSide.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BinaryExpr binary) {
|
public void visit(BinaryExpr binary) {
|
||||||
|
binary.lexpr.accept(this);
|
||||||
|
binary.rexpr.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Block block) {
|
public void visit(Block block) {
|
||||||
|
for (var expr : block.statements)
|
||||||
|
expr.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -47,12 +58,13 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ForStmt forStmt) {
|
public void visit(ForStmt forStmt) {
|
||||||
|
forStmt.body_Loop_block.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(IfStmt ifStmt) {
|
public void visit(IfStmt ifStmt) {
|
||||||
|
ifStmt.then_block.accept(this);
|
||||||
|
ifStmt.else_block.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -71,23 +83,18 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodCall methodCall) {
|
public void visit(NewClass newClass) {
|
||||||
|
this.visit((MethodCall) newClass);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NewClass methodCall) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(NewArray newArray) {
|
public void visit(NewArray newArray) {
|
||||||
|
newArray.expr.forEach(expr -> expr.accept(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Return aReturn) {
|
public void visit(Return aReturn) {
|
||||||
|
aReturn.retexpr.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -112,12 +119,12 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WhileStmt whileStmt) {
|
public void visit(WhileStmt whileStmt) {
|
||||||
|
whileStmt.loopBlock.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(DoStmt whileStmt) {
|
public void visit(DoStmt whileStmt) {
|
||||||
|
whileStmt.loopBlock.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -142,7 +149,7 @@ public abstract class AbstractStatementVisitor implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(UnaryExpr unaryExpr) {
|
public void visit(UnaryExpr unaryExpr) {
|
||||||
|
unaryExpr.expr.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
Loading…
Reference in New Issue
Block a user