Add new target ast nodes and test cases for switch

This commit is contained in:
Daniel Holle 2023-07-26 15:47:30 +02:00
parent a0582e918b
commit 3de9fde672
15 changed files with 240 additions and 243 deletions

View File

@ -11,20 +11,20 @@ public class SwitchBlock extends Block {
private List<SwitchLabel> labels = new ArrayList<>(); private List<SwitchLabel> labels = new ArrayList<>();
private Boolean defaultBlock = false; private boolean defaultBlock = false;
public SwitchBlock(List<SwitchLabel> labels, Block statements, Token offset) { public SwitchBlock(List<SwitchLabel> labels, Block statements, Token offset) {
super(statements.getStatements(), offset); super(statements.getStatements(), offset);
this.labels = labels; this.labels = labels;
} }
public SwitchBlock(List<SwitchLabel> labels, Block statements, Boolean isDefault, Token offset) { public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isDefault, Token offset) {
super(statements.getStatements(), offset); super(statements.getStatements(), offset);
this.labels = labels; this.labels = labels;
this.defaultBlock = isDefault; this.defaultBlock = isDefault;
} }
public Boolean isDefault() { public boolean isDefault() {
return defaultBlock; return defaultBlock;
} }

View File

@ -10,9 +10,11 @@ import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.target.tree.*; import de.dhbwstuttgart.target.tree.*;
import de.dhbwstuttgart.target.tree.expression.TargetBlock; import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.expression.TargetExpression; import de.dhbwstuttgart.target.tree.expression.TargetExpression;
import de.dhbwstuttgart.target.tree.expression.TargetSwitch;
import de.dhbwstuttgart.target.tree.type.*; import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*; import de.dhbwstuttgart.typeinference.result.*;
import java.lang.annotation.Target;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -205,6 +207,10 @@ public class ASTToTargetAST {
return result; return result;
} }
protected TargetSwitch.Case convert(SwitchBlock block) {
return new TargetSwitch.Case(block.getLabels().stream().map(this::convert).toList(), convert((Block) block));
}
protected TargetBlock convert(Block block) { protected TargetBlock convert(Block block) {
return new TargetBlock(block.statements.stream().map(this::convert).toList()); return new TargetBlock(block.statements.stream().map(this::convert).toList());
} }

View File

@ -74,41 +74,6 @@ public class StatementToTargetExpression implements StatementVisitor {
@Override @Override
public void visit(LambdaExpression lambda) { public void visit(LambdaExpression lambda) {
} // Don't look at lambda expressions } // Don't look at lambda expressions
@Override
public void visit(Switch switchStmt) {
// TODO Auto-generated method stub
}
@Override
public void visit(SwitchBlock switchBlock) {
// TODO Auto-generated method stub
}
@Override
public void visit(SwitchLabel switchLabel) {
// TODO Auto-generated method stub
}
@Override
public void visit(Yield aYield) {
// TODO Auto-generated method stub
}
@Override
public void visit(Pattern aPattern) {
// TODO Auto-generated method stub
}
@Override
public void visit(RecordPattern aRecordPattern) {
// TODO Auto-generated method stub
}
@Override
public void visit(GuardedPattern aGuardedPattern) {
// TODO Auto-generated method stub
}
}); });
result = new TargetLambdaExpression(converter.convert(lambdaExpression.getType()), captures, parameters, converter.convert(lambdaExpression.getReturnType()), converter.convert(lambdaExpression.methodBody)); result = new TargetLambdaExpression(converter.convert(lambdaExpression.getType()), captures, parameters, converter.convert(lambdaExpression.getReturnType()), converter.convert(lambdaExpression.methodBody));
@ -378,18 +343,16 @@ public class StatementToTargetExpression implements StatementVisitor {
@Override @Override
public void visit(Switch switchStmt) { public void visit(Switch switchStmt) {
// TODO Auto-generated method stub var cases = switchStmt.getBlocks().stream().filter(s -> !s.isDefault()).map(converter::convert).toList();
var default_ = switchStmt.getBlocks().stream().filter(SwitchBlock::isDefault).map(s -> converter.convert((Block) s)).findFirst().orElse(null);
result = new TargetSwitch(converter.convert(switchStmt.getSwitch()), cases, default_, converter.convert(switchStmt.getType()), !switchStmt.getStatement());
} }
@Override @Override
public void visit(SwitchBlock switchBlock) { public void visit(SwitchBlock switchBlock) {}
// TODO Auto-generated method stub
}
@Override @Override
public void visit(SwitchLabel switchLabel) { public void visit(SwitchLabel switchLabel) {}
// TODO Auto-generated method stub
}
@Override @Override
public void visit(Yield aYield) { public void visit(Yield aYield) {
@ -398,16 +361,21 @@ public class StatementToTargetExpression implements StatementVisitor {
@Override @Override
public void visit(Pattern aPattern) { public void visit(Pattern aPattern) {
// TODO Auto-generated method stub result = new TargetSwitch.Pattern(converter.convert(aPattern.getType()), aPattern.getName());
} }
@Override @Override
public void visit(RecordPattern aRecordPattern) { public void visit(RecordPattern aRecordPattern) {
// TODO Auto-generated method stub result = new TargetSwitch.ComplexPattern(
converter.convert(aRecordPattern.getType()),
aRecordPattern.getSubPattern().stream().map(x -> (TargetSwitch.Pattern) converter.convert(x)).toList()
);
} }
@Override @Override
public void visit(GuardedPattern aGuardedPattern) { public void visit(GuardedPattern aGuardedPattern) {
// TODO Auto-generated method stub //FIXME This isn't done properly inside the parser, really you should only have one guard (Chaining them together with && just yields another expression)
//And then it also needs to be able to accept complex patterns. Because of this we only accept one condition for now.
result = new TargetSwitch.Guard(new TargetSwitch.Pattern(converter.convert(aGuardedPattern.getType()), aGuardedPattern.getName()), converter.convert(aGuardedPattern.getConditions().get(0)));
} }
} }

View File

@ -5,8 +5,4 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List; import java.util.List;
public record TargetBlock(List<TargetExpression> statements) implements TargetExpression { public record TargetBlock(List<TargetExpression> statements) implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -3,9 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetBreak() implements TargetExpression { public record TargetBreak() implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -3,9 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetContinue() implements TargetExpression { public record TargetContinue() implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -3,7 +3,9 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.*; import de.dhbwstuttgart.target.tree.type.*;
public sealed interface TargetExpression public sealed interface TargetExpression
permits TargetBinaryOp, TargetBlock, TargetBreak, TargetCast, TargetClassName, TargetContinue, TargetFieldVar, TargetFor, TargetForEach, TargetIf, TargetInstanceOf, TargetLambdaExpression, TargetLiteral, TargetLocalVar, TargetReturn, TargetStatementExpression, TargetSuper, TargetSwitch, TargetTernary, TargetThis, TargetUnaryOp, TargetVarDecl, TargetWhile { permits TargetBinaryOp, TargetBlock, TargetBreak, TargetCast, TargetClassName, TargetContinue, TargetFieldVar, TargetFor, TargetForEach, TargetIf, TargetInstanceOf, TargetLambdaExpression, TargetLiteral, TargetLocalVar, TargetReturn, TargetStatementExpression, TargetSuper, TargetSwitch, TargetSwitch.ComplexPattern, TargetSwitch.Guard, TargetSwitch.Pattern, TargetTernary, TargetThis, TargetUnaryOp, TargetVarDecl, TargetWhile, TargetYield {
TargetType type(); default TargetType type() {
return null;
};
} }

View File

@ -3,9 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetFor(TargetExpression init, TargetExpression termination, TargetExpression increment, TargetExpression body) implements TargetExpression { public record TargetFor(TargetExpression init, TargetExpression termination, TargetExpression increment, TargetExpression body) implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -3,9 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetForEach(TargetExpression vardecl, TargetExpression list) implements TargetExpression { public record TargetForEach(TargetExpression vardecl, TargetExpression list) implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -3,8 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetIf(TargetExpression cond, TargetExpression if_body, TargetExpression else_body) implements TargetExpression { public record TargetIf(TargetExpression cond, TargetExpression if_body, TargetExpression else_body) implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -3,8 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetReturn(TargetExpression expression) implements TargetExpression { public record TargetReturn(TargetExpression expression) implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -1,16 +1,27 @@
package de.dhbwstuttgart.target.tree.expression; package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.syntaxtree.statement.Expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List; import java.util.List;
public record TargetSwitch(Expression expr, List<Case> cases, Expression default_) implements TargetExpression { public record TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, TargetType type, boolean isExpression) implements TargetExpression {
@Override public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_) {
public TargetType type() { this(expr, cases, default_, null, false);
return null;
} }
record Case(Expression value, Expression body) {} public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, TargetType type) {
this(expr, cases, default_, type, true);
}
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, boolean isExpression) {
this(expr, cases, default_, null, isExpression);
}
public record Case(List<TargetExpression> labels, TargetBlock body) {}
public record Pattern(TargetType type, String name) implements TargetExpression {}
public record ComplexPattern(TargetType type, List<Pattern> subPatterns) implements TargetExpression {}
public record Guard(TargetExpression inner, TargetExpression expression) implements TargetExpression {}
} }

View File

@ -3,9 +3,4 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetWhile(TargetExpression cond, TargetExpression body) implements TargetExpression { public record TargetWhile(TargetExpression cond, TargetExpression body) implements TargetExpression {
@Override
public TargetType type() {
return null;
}
} }

View File

@ -0,0 +1,4 @@
package de.dhbwstuttgart.target.tree.expression;
public record TargetYield(TargetExpression expression) implements TargetExpression {
}

View File

@ -13,7 +13,9 @@ import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import java.io.IOException; import java.io.IOException;
@ -196,6 +198,47 @@ public class TestCodegen {
assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10); assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10);
} }
@Test
public void testClassicSwitch() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , "Switch");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "switchClassic", List.of(new MethodParameter(TargetType.Integer, "i")), TargetType.Integer, new TargetBlock(List.of(
new TargetVarDecl(TargetType.Integer, "res", null),
new TargetSwitch(new TargetLocalVar(TargetType.Integer, "i"), List.of(
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(10)), new TargetBlock(
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(0)), new TargetBreak())
)),
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(20)), new TargetBlock(List.of())),
new TargetSwitch.Case(List.of(new TargetLiteral.IntLiteral(30)), new TargetBlock(
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(1)), new TargetBreak())
))
), new TargetBlock(
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(2)), new TargetBreak())
))
)));
}
@Test
public void testTypeSwitch() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Switch");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "switchType", List.of(new MethodParameter(TargetType.Object, "obj")), TargetType.Integer, new TargetBlock(List.of(
new TargetReturn(new TargetSwitch(new TargetLocalVar(TargetType.Object, "obj"), List.of(
new TargetSwitch.Case(List.of(new TargetSwitch.Pattern(TargetType.String, "aString")), new TargetBlock(
List.of(new TargetLiteral.IntLiteral(0))
)),
new TargetSwitch.Case(List.of(new TargetSwitch.Pattern(TargetType.Integer, "anInteger")), new TargetBlock(
List.of(new TargetLiteral.IntLiteral(1))
))
), new TargetBlock(
List.of(new TargetLiteral.IntLiteral(2))
), TargetType.Integer)
))));
var clazz = generateClass(targetClass, new ByteArrayClassLoader());
var m = clazz.getDeclaredMethod("switchType", Object.class);
assertEquals(m.invoke(null, "String"), 0);
assertEquals(m.invoke(null, 10), 1);
assertEquals(m.invoke(null, 'A'), 2);
}
@Test @Test
@Ignore("The lambda class is not generated because we don't call ASTToTargetAST") @Ignore("The lambda class is not generated because we don't call ASTToTargetAST")
public void testLambda() throws Exception { public void testLambda() throws Exception {