Allow the first patterns
This commit is contained in:
parent
be55d661cb
commit
4f3164a48a
@ -717,10 +717,9 @@ switchLabelCase
|
||||
| DEFAULT (ARROW | COLON) #labeledRuleDefault
|
||||
;
|
||||
|
||||
// Java17
|
||||
// Java20
|
||||
guardedPattern
|
||||
: variableModifier* typeType annotation* identifier ('&&' expression)*
|
||||
| guardedPattern '&&' expression
|
||||
: primaryPattern WITH expression
|
||||
;
|
||||
|
||||
// Java17
|
||||
|
@ -5,6 +5,7 @@ import de.dhbwstuttgart.syntaxtree.statement.Break;
|
||||
import de.dhbwstuttgart.target.tree.*;
|
||||
import de.dhbwstuttgart.target.tree.expression.*;
|
||||
import de.dhbwstuttgart.target.tree.type.*;
|
||||
import org.antlr.v4.codegen.Target;
|
||||
import org.objectweb.asm.*;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
@ -71,6 +72,7 @@ public class Codegen {
|
||||
TargetType returnType;
|
||||
|
||||
Stack<BreakEnv> breakStack = new Stack<>();
|
||||
Stack<Integer> switchResultValue = new Stack<>();
|
||||
|
||||
State(TargetType returnType, MethodVisitor mv, int localCounter) {
|
||||
this.returnType = returnType;
|
||||
@ -92,6 +94,13 @@ public class Codegen {
|
||||
localCounter += 1;
|
||||
return local;
|
||||
}
|
||||
|
||||
void pushSwitch() {
|
||||
switchResultValue.push(this.localCounter++);
|
||||
}
|
||||
void popSwitch() {
|
||||
switchResultValue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private void popValue(State state, TargetType type) {
|
||||
@ -582,11 +591,10 @@ public class Codegen {
|
||||
private void generateUnaryOp(State state, TargetUnaryOp op) {
|
||||
var mv = state.mv;
|
||||
switch (op) {
|
||||
case TargetUnaryOp.Add add:
|
||||
case TargetUnaryOp.Add add ->
|
||||
// This literally does nothing
|
||||
generate(state, add.expr());
|
||||
break;
|
||||
case TargetUnaryOp.Negate negate:
|
||||
case TargetUnaryOp.Negate negate -> {
|
||||
generate(state, negate.expr());
|
||||
if (negate.type().equals(TargetType.Double))
|
||||
mv.visitInsn(DNEG);
|
||||
@ -596,8 +604,8 @@ public class Codegen {
|
||||
mv.visitInsn(LNEG);
|
||||
else
|
||||
mv.visitInsn(INEG);
|
||||
break;
|
||||
case TargetUnaryOp.Not not:
|
||||
}
|
||||
case TargetUnaryOp.Not not -> {
|
||||
generate(state, not.expr());
|
||||
if (not.type().equals(TargetType.Long)) {
|
||||
mv.visitLdcInsn(-1L);
|
||||
@ -606,8 +614,8 @@ public class Codegen {
|
||||
mv.visitInsn(ICONST_M1);
|
||||
mv.visitInsn(IXOR);
|
||||
}
|
||||
break;
|
||||
case TargetUnaryOp.PreIncrement preIncrement:
|
||||
}
|
||||
case TargetUnaryOp.PreIncrement preIncrement -> {
|
||||
generate(state, preIncrement.expr());
|
||||
if (preIncrement.type().equals(TargetType.Float)) {
|
||||
mv.visitLdcInsn(1F);
|
||||
@ -628,8 +636,8 @@ public class Codegen {
|
||||
}
|
||||
boxPrimitive(state, preIncrement.type());
|
||||
afterIncDec(state, preIncrement);
|
||||
break;
|
||||
case TargetUnaryOp.PreDecrement preDecrement:
|
||||
}
|
||||
case TargetUnaryOp.PreDecrement preDecrement -> {
|
||||
generate(state, preDecrement.expr());
|
||||
if (preDecrement.type().equals(TargetType.Float)) {
|
||||
mv.visitLdcInsn(1F);
|
||||
@ -650,8 +658,8 @@ public class Codegen {
|
||||
}
|
||||
boxPrimitive(state, preDecrement.type());
|
||||
afterIncDec(state, preDecrement);
|
||||
break;
|
||||
case TargetUnaryOp.PostIncrement postIncrement:
|
||||
}
|
||||
case TargetUnaryOp.PostIncrement postIncrement -> {
|
||||
generate(state, postIncrement.expr());
|
||||
if (postIncrement.type().equals(TargetType.Float)) {
|
||||
mv.visitInsn(DUP);
|
||||
@ -672,8 +680,8 @@ public class Codegen {
|
||||
}
|
||||
boxPrimitive(state, postIncrement.type());
|
||||
afterIncDec(state, postIncrement);
|
||||
break;
|
||||
case TargetUnaryOp.PostDecrement postDecrement:
|
||||
}
|
||||
case TargetUnaryOp.PostDecrement postDecrement -> {
|
||||
generate(state, postDecrement.expr());
|
||||
if (postDecrement.type().equals(TargetType.Float)) {
|
||||
mv.visitInsn(DUP);
|
||||
@ -694,7 +702,7 @@ public class Codegen {
|
||||
}
|
||||
boxPrimitive(state, postDecrement.type());
|
||||
afterIncDec(state, postDecrement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,31 +781,19 @@ public class Codegen {
|
||||
break;
|
||||
case TargetLiteral literal:
|
||||
switch (literal) {
|
||||
case IntLiteral intLiteral:
|
||||
mv.visitLdcInsn(intLiteral.value());
|
||||
break;
|
||||
case FloatLiteral floatLiteral:
|
||||
mv.visitLdcInsn(floatLiteral.value());
|
||||
break;
|
||||
case LongLiteral longLiteral:
|
||||
mv.visitLdcInsn(longLiteral.value());
|
||||
break;
|
||||
case StringLiteral stringLiteral:
|
||||
mv.visitLdcInsn(stringLiteral.value());
|
||||
break;
|
||||
case CharLiteral charLiteral:
|
||||
mv.visitIntInsn(BIPUSH, charLiteral.value());
|
||||
break;
|
||||
case DoubleLiteral doubleLiteral:
|
||||
mv.visitLdcInsn(doubleLiteral.value());
|
||||
break;
|
||||
case BooleanLiteral booleanLiteral:
|
||||
case IntLiteral intLiteral -> mv.visitLdcInsn(intLiteral.value());
|
||||
case FloatLiteral floatLiteral -> mv.visitLdcInsn(floatLiteral.value());
|
||||
case LongLiteral longLiteral -> mv.visitLdcInsn(longLiteral.value());
|
||||
case StringLiteral stringLiteral -> mv.visitLdcInsn(stringLiteral.value());
|
||||
case CharLiteral charLiteral -> mv.visitIntInsn(BIPUSH, charLiteral.value());
|
||||
case DoubleLiteral doubleLiteral -> mv.visitLdcInsn(doubleLiteral.value());
|
||||
case BooleanLiteral booleanLiteral -> {
|
||||
if (booleanLiteral.value()) {
|
||||
mv.visitInsn(ICONST_1);
|
||||
} else {
|
||||
mv.visitInsn(ICONST_0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TargetVarDecl varDecl: {
|
||||
@ -820,16 +816,15 @@ public class Codegen {
|
||||
break;
|
||||
case TargetAssign assign: {
|
||||
switch (assign.left()) {
|
||||
case TargetLocalVar localVar: {
|
||||
case TargetLocalVar localVar -> {
|
||||
generate(state, assign.right());
|
||||
convertTo(state, assign.right().type(), localVar.type());
|
||||
boxPrimitive(state, localVar.type());
|
||||
var local = state.scope.get(localVar.name());
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitVarInsn(ASTORE, local.index());
|
||||
break;
|
||||
}
|
||||
case TargetFieldVar dot: {
|
||||
case TargetFieldVar dot -> {
|
||||
var fieldType = dot.type();
|
||||
generate(state, dot.left());
|
||||
generate(state, assign.right());
|
||||
@ -840,10 +835,8 @@ public class Codegen {
|
||||
else
|
||||
mv.visitInsn(DUP_X1);
|
||||
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.owner().getInternalName(), dot.right(), fieldType.toSignature());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new CodeGenException("Invalid assignment");
|
||||
default -> throw new CodeGenException("Invalid assignment");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -890,7 +883,6 @@ public class Codegen {
|
||||
}
|
||||
mv.visitJumpInsn(GOTO, start);
|
||||
mv.visitLabel(end);
|
||||
mv.visitInsn(NOP);
|
||||
state.exitScope();
|
||||
state.localCounter = localCounter;
|
||||
break;
|
||||
@ -912,7 +904,6 @@ public class Codegen {
|
||||
|
||||
mv.visitJumpInsn(GOTO, start);
|
||||
mv.visitLabel(end);
|
||||
mv.visitInsn(NOP);
|
||||
break;
|
||||
}
|
||||
case TargetIf _if: {
|
||||
@ -927,7 +918,6 @@ public class Codegen {
|
||||
generate(state, _if.else_body());
|
||||
}
|
||||
mv.visitLabel(end);
|
||||
mv.visitInsn(NOP);
|
||||
break;
|
||||
}
|
||||
case TargetReturn ret: {
|
||||
@ -940,6 +930,16 @@ public class Codegen {
|
||||
mv.visitInsn(RETURN);
|
||||
break;
|
||||
}
|
||||
case TargetYield yield: {
|
||||
generate(state, yield.expression());
|
||||
try {
|
||||
yieldValue(state, yield.expression().type());
|
||||
mv.visitJumpInsn(GOTO, state.breakStack.peek().endLabel);
|
||||
} catch (EmptyStackException e) {
|
||||
throw new CodeGenException("Yield outside of switch expression");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TargetSwitch _switch: {
|
||||
generateSwitch(state, _switch);
|
||||
break;
|
||||
@ -1004,13 +1004,25 @@ public class Codegen {
|
||||
}
|
||||
}
|
||||
|
||||
private void yieldValue(State state, TargetType type) {
|
||||
boxPrimitive(state, type);
|
||||
state.mv.visitVarInsn(ASTORE, state.switchResultValue.peek());
|
||||
}
|
||||
|
||||
private void generateClassicSwitch(State state, TargetSwitch aSwitch) {
|
||||
// TODO Constant expressions are allowed, we need to evaluate them somehow...
|
||||
// For now we just assume we get literals
|
||||
// TODO This always uses a lookupswitch, a tableswitch may be faster in some cases but we can't generate that in all cases
|
||||
// For now we just assume we get literals...
|
||||
// TODO This always uses a lookupswitch, a tableswitch may be faster in some cases but we can't generate that every time
|
||||
// TODO We can't switch on Strings yet, the idea for this (like javac does it) would be to implement the hash code at compile time
|
||||
// and switch based on that, adding an equals check for every case and going to yet another tableswitch which finally decides which branch to take
|
||||
var mv = state.mv;
|
||||
if (aSwitch.isExpression())
|
||||
state.pushSwitch();
|
||||
|
||||
generate(state, aSwitch.expr());
|
||||
|
||||
state.enterScope();
|
||||
|
||||
var keys = new int[aSwitch.cases().stream().mapToInt(c -> c.labels().size()).sum()];
|
||||
var labels = new Label[keys.length];
|
||||
var bodyLabels = new Label[aSwitch.cases().size()];
|
||||
@ -1043,28 +1055,159 @@ public class Codegen {
|
||||
|
||||
for (var k = 0; k < aSwitch.cases().size(); k++) {
|
||||
mv.visitLabel(bodyLabels[k]);
|
||||
generate(state, aSwitch.cases().get(k).body());
|
||||
var cse = aSwitch.cases().get(k);
|
||||
generate(state, cse.body());
|
||||
if (cse.isSingleExpression() && aSwitch.isExpression())
|
||||
yieldValue(state, cse.body().statements().get(0).type());
|
||||
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
|
||||
}
|
||||
|
||||
if (aSwitch.default_() != null) {
|
||||
mv.visitLabel(defaultLabel);
|
||||
generate(state, aSwitch.default_());
|
||||
generate(state, aSwitch.default_().body());
|
||||
if (aSwitch.default_().isSingleExpression() && aSwitch.isExpression())
|
||||
yieldValue(state, aSwitch.default_().body().statements().get(0).type());
|
||||
}
|
||||
|
||||
mv.visitLabel(end);
|
||||
mv.visitInsn(NOP);
|
||||
state.breakStack.pop();
|
||||
|
||||
if (aSwitch.isExpression()) {
|
||||
mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
|
||||
unboxPrimitive(state, aSwitch.type());
|
||||
state.popSwitch();
|
||||
}
|
||||
|
||||
state.exitScope();
|
||||
}
|
||||
|
||||
private void generateEnhancedSwitch(State state, TargetSwitch aSwitch) {
|
||||
var mv = state.mv;
|
||||
generate(state, aSwitch.expr());
|
||||
var tmp = state.localCounter++;
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitVarInsn(ASTORE, tmp);
|
||||
|
||||
state.enterScope();
|
||||
// This is the index to start the switch from
|
||||
mv.visitInsn(ICONST_0);
|
||||
if (aSwitch.isExpression())
|
||||
state.pushSwitch();
|
||||
|
||||
// To be able to skip ahead to the next case
|
||||
var start = new Label();
|
||||
mv.visitLabel(start);
|
||||
|
||||
var end = new Label();
|
||||
var env = new BreakEnv();
|
||||
env.endLabel = end;
|
||||
state.breakStack.push(env);
|
||||
|
||||
var mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class);
|
||||
var bootstrap = new Handle(H_INVOKESTATIC, "java/lang/runtime/SwitchBootstraps", "typeSwitch", mt.toMethodDescriptorString(), false);
|
||||
|
||||
var types = new Object[aSwitch.cases().size()];
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
var cse = aSwitch.cases().get(i);
|
||||
var label = cse.labels().get(0);
|
||||
if (label instanceof TargetSwitch.SimplePattern || label instanceof TargetSwitch.ComplexPattern)
|
||||
types[i] = Type.getObjectType(label.type().getInternalName());
|
||||
else if (label instanceof TargetLiteral lit)
|
||||
types[i] = lit.value();
|
||||
else if (label instanceof TargetSwitch.Guard guard)
|
||||
types[i] = Type.getObjectType(guard.inner().type().getInternalName());
|
||||
// TODO Same here we need to evaluate constants
|
||||
else throw new NotImplementedException();
|
||||
}
|
||||
|
||||
mv.visitInvokeDynamicInsn("typeSwitch", "(Ljava/lang/Object;I)I", bootstrap, types);
|
||||
|
||||
var caseLabels = new Label[aSwitch.cases().size()];
|
||||
var labels = new Label[aSwitch.cases().stream().mapToInt(c -> c.labels().size()).sum()];
|
||||
var j = 0;
|
||||
for (var i = 0; i < caseLabels.length; i++) {
|
||||
var cse = aSwitch.cases().get(i);
|
||||
var label = new Label();
|
||||
caseLabels[i] = label;
|
||||
for (var k = 0; k < cse.labels().size(); k++) {
|
||||
labels[j] = label;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
var defaultLabel = end;
|
||||
if (aSwitch.default_() != null) {
|
||||
defaultLabel = new Label();
|
||||
}
|
||||
|
||||
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
|
||||
|
||||
for (var i = 0; i < aSwitch.cases().size(); i++) {
|
||||
mv.visitLabel(caseLabels[i]);
|
||||
var cse = aSwitch.cases().get(i);
|
||||
|
||||
if (cse.labels().size() == 1) {
|
||||
var label = cse.labels().get(0);
|
||||
if (label instanceof TargetSwitch.Guard gd)
|
||||
bindLabel(state, tmp, aSwitch.expr().type(), gd.inner());
|
||||
else if (label instanceof TargetSwitch.Pattern pat)
|
||||
bindLabel(state, tmp, aSwitch.expr().type(), pat);
|
||||
|
||||
if (label instanceof TargetSwitch.Guard gd) {
|
||||
generate(state, gd.expression());
|
||||
var next = new Label();
|
||||
mv.visitJumpInsn(IFNE, next);
|
||||
mv.visitVarInsn(ALOAD, tmp);
|
||||
// Push the offset onto the stack (this is used by the invokedynamic call)
|
||||
mv.visitLdcInsn(i + 1);
|
||||
mv.visitJumpInsn(GOTO, start);
|
||||
mv.visitLabel(next);
|
||||
}
|
||||
}
|
||||
|
||||
generate(state, cse.body());
|
||||
if (cse.isSingleExpression() && aSwitch.isExpression())
|
||||
yieldValue(state, cse.body().statements().get(0).type());
|
||||
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
|
||||
}
|
||||
|
||||
if (aSwitch.default_() != null) {
|
||||
mv.visitLabel(defaultLabel);
|
||||
generate(state, aSwitch.default_().body());
|
||||
if (aSwitch.default_().isSingleExpression() && aSwitch.isExpression())
|
||||
yieldValue(state, aSwitch.default_().body().statements().get(0).type());
|
||||
}
|
||||
|
||||
mv.visitLabel(end);
|
||||
//mv.visitInsn(POP);
|
||||
|
||||
state.breakStack.pop();
|
||||
if (aSwitch.isExpression()) {
|
||||
mv.visitVarInsn(ALOAD, state.switchResultValue.peek());
|
||||
unboxPrimitive(state, aSwitch.type());
|
||||
state.popSwitch();
|
||||
}
|
||||
|
||||
state.exitScope();
|
||||
}
|
||||
|
||||
private void bindLabel(State state, int tmp, TargetType type, TargetSwitch.Pattern pat) {
|
||||
if (pat instanceof TargetSwitch.SimplePattern sp) {
|
||||
state.mv.visitVarInsn(ALOAD, tmp);
|
||||
var local = state.createVariable(sp.name(), sp.type());
|
||||
convertTo(state, type, local.type);
|
||||
boxPrimitive(state, local.type);
|
||||
state.mv.visitVarInsn(ASTORE, local.index);
|
||||
}
|
||||
}
|
||||
|
||||
final Set<TargetType> wrapperTypes = Set.of(TargetType.Long, TargetType.Integer, TargetType.Byte, TargetType.Char, TargetType.Boolean, TargetType.Double, TargetType.Float);
|
||||
|
||||
private void generateSwitch(State state, TargetSwitch aSwitch) {
|
||||
if (!wrapperTypes.contains(aSwitch.expr().type()))
|
||||
if (!wrapperTypes.contains(aSwitch.expr().type())) {
|
||||
generateEnhancedSwitch(state, aSwitch);
|
||||
return;
|
||||
}
|
||||
else for (var case_ : aSwitch.cases()) {
|
||||
if (case_.labels().stream().anyMatch(c -> c instanceof TargetSwitch.Pattern)) {
|
||||
generateEnhancedSwitch(state, aSwitch);
|
||||
|
@ -20,7 +20,6 @@ import de.dhbwstuttgart.parser.antlr.Java17Parser.AssignexpressionContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseandexpressionContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwiseorexpressionContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BitwisexorexpressionContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BlockStatementContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BlockstmtContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BoolLiteralContext;
|
||||
import de.dhbwstuttgart.parser.antlr.Java17Parser.BreakstmtContext;
|
||||
@ -381,7 +380,7 @@ public class StatementGenerator {
|
||||
return new Switch(switched, switchBlocks, TypePlaceholder.fresh(offset), false, offset);
|
||||
}
|
||||
|
||||
private SwitchBlock convert(Java17Parser.SwitchLabeledRuleContext labeledRule) {
|
||||
private SwitchBlock convert(SwitchLabeledRuleContext labeledRule) {
|
||||
Boolean isDefault = false;
|
||||
List<SwitchLabel> labels = switch (labeledRule.switchLabelCase()) {
|
||||
case LabeledRuleExprListContext exprList -> {
|
||||
@ -401,11 +400,14 @@ public class StatementGenerator {
|
||||
}
|
||||
default -> throw new NotImplementedException();
|
||||
};
|
||||
|
||||
var isSingleExpression = false;
|
||||
Token offset = labeledRule.getStart();
|
||||
SwitchRuleOutcomeContext outcome = labeledRule.switchRuleOutcome();
|
||||
Block block;
|
||||
if (Objects.isNull(outcome.block())) {
|
||||
List<Statement> stmts = new ArrayList<>();
|
||||
if (outcome.blockStatement().size() == 1) isSingleExpression = true;
|
||||
outcome.blockStatement().stream().forEach((stmt) -> {
|
||||
stmts.addAll(convert(stmt));
|
||||
});
|
||||
@ -414,7 +416,7 @@ public class StatementGenerator {
|
||||
} else {
|
||||
block = convert(outcome.block(), false);
|
||||
}
|
||||
return new SwitchBlock(labels, block, isDefault, offset);
|
||||
return new SwitchBlock(labels, block, isDefault, isSingleExpression, offset);
|
||||
}
|
||||
|
||||
private Statement convert(Java17Parser.YieldstmtContext yieldstmt) {
|
||||
@ -430,7 +432,7 @@ public class StatementGenerator {
|
||||
stmt.blockStatement().stream().forEach((blockStmt) -> {
|
||||
block.addAll(convert(blockStmt));
|
||||
});
|
||||
return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), stmt.getStart());
|
||||
return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), false, stmt.getStart());
|
||||
}
|
||||
|
||||
private SwitchLabel convert(SwitchLabelContext switchLabel) {
|
||||
@ -454,17 +456,15 @@ public class StatementGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private Pattern convert(PatternContext pattern) {
|
||||
private Expression convert(PatternContext pattern) {
|
||||
return switch (pattern) {
|
||||
case PPatternContext pPattern -> {
|
||||
yield convert(pPattern.primaryPattern());
|
||||
}
|
||||
case GPatternContext gPattern -> {
|
||||
GuardedPatternContext guarded = gPattern.guardedPattern();
|
||||
List<Expression> conditions = guarded.expression().stream().map((expr) -> {
|
||||
return convert(expr);
|
||||
}).toList();
|
||||
yield new GuardedPattern(conditions, guarded.identifier().getText(), TypeGenerator.convert(guarded.typeType(), reg, generics), guarded.getStart());
|
||||
Expression condition = convert(guarded.expression());
|
||||
yield new GuardedPattern(condition, convert(guarded.primaryPattern()), guarded.getStart());
|
||||
}
|
||||
default -> throw new NotImplementedException();
|
||||
};
|
||||
@ -487,7 +487,7 @@ public class StatementGenerator {
|
||||
private RecordPattern convert(RecordPatternContext recordPatternCtx) {
|
||||
List<PatternContext> subPatternCtx = recordPatternCtx.recordStructurePattern().recordComponentPatternList().pattern();
|
||||
List<Pattern> subPattern = subPatternCtx.stream().map((patternCtx) -> {
|
||||
return convert(patternCtx);
|
||||
return (Pattern) convert(patternCtx);
|
||||
}).collect(Collectors.toList());
|
||||
IdentifierContext identifierCtx = recordPatternCtx.identifier();
|
||||
return new RecordPattern(subPattern, (identifierCtx != null) ? identifierCtx.getText() : null, TypeGenerator.convert(recordPatternCtx.typeType(), reg, generics), recordPatternCtx.getStart());
|
||||
|
@ -2,21 +2,32 @@ package de.dhbwstuttgart.syntaxtree.statement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
||||
|
||||
public class GuardedPattern extends Pattern {
|
||||
public class GuardedPattern extends Expression {
|
||||
|
||||
private List<Expression> conditions;
|
||||
private final Expression condition;
|
||||
private final Pattern nested;
|
||||
|
||||
public GuardedPattern(List<Expression> conditions, String name, RefTypeOrTPHOrWildcardOrGeneric type, Token offset) {
|
||||
super(name, type, offset);
|
||||
this.conditions = conditions;
|
||||
public GuardedPattern(Expression condition, Pattern nested, Token offset) {
|
||||
super(nested.getType(), offset);
|
||||
this.condition = condition;
|
||||
this.nested = nested;
|
||||
}
|
||||
|
||||
public List<Expression> getConditions() {
|
||||
return conditions;
|
||||
public Expression getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public Pattern getNestedPattern() {
|
||||
return nested;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(StatementVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,19 @@ public class SwitchBlock extends Block {
|
||||
private List<SwitchLabel> labels = new ArrayList<>();
|
||||
|
||||
private boolean defaultBlock = false;
|
||||
public final boolean isExpression; // This is for single expressions that yield a value
|
||||
|
||||
public SwitchBlock(List<SwitchLabel> labels, Block statements, Token offset) {
|
||||
public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isExpression, Token offset) {
|
||||
super(statements.getStatements(), offset);
|
||||
this.labels = labels;
|
||||
this.isExpression = isExpression;
|
||||
}
|
||||
|
||||
public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isDefault, Token offset) {
|
||||
public SwitchBlock(List<SwitchLabel> labels, Block statements, boolean isDefault, boolean isExpression, Token offset) {
|
||||
super(statements.getStatements(), offset);
|
||||
this.labels = labels;
|
||||
this.defaultBlock = isDefault;
|
||||
this.isExpression = isExpression;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
|
@ -474,11 +474,8 @@ public class OutputGenerator implements ASTVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(GuardedPattern aGuardedPattern) {
|
||||
aGuardedPattern.getType().accept(this);
|
||||
out.append(aGuardedPattern.getName());
|
||||
for (Expression cond : aGuardedPattern.getConditions()) {
|
||||
out.append("&&");
|
||||
cond.accept(this);
|
||||
}
|
||||
aGuardedPattern.getNestedPattern().accept(this);
|
||||
out.append(" with ");
|
||||
aGuardedPattern.getCondition().accept(this);
|
||||
}
|
||||
}
|
@ -208,7 +208,7 @@ public class ASTToTargetAST {
|
||||
}
|
||||
|
||||
protected TargetSwitch.Case convert(SwitchBlock block) {
|
||||
return new TargetSwitch.Case(block.getLabels().stream().map(this::convert).toList(), convert((Block) block));
|
||||
return new TargetSwitch.Case(block.getLabels().stream().map(this::convert).toList(), convert((Block) block), block.isExpression);
|
||||
}
|
||||
|
||||
protected TargetBlock convert(Block block) {
|
||||
|
@ -344,8 +344,14 @@ public class StatementToTargetExpression implements StatementVisitor {
|
||||
@Override
|
||||
public void visit(Switch switchStmt) {
|
||||
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());
|
||||
|
||||
TargetSwitch.Case default_ = null;
|
||||
for (var block : switchStmt.getBlocks()) {
|
||||
if (block.isDefault()) {
|
||||
default_ = new TargetSwitch.Case(converter.convert((Block) block), block.isExpression);
|
||||
}
|
||||
}
|
||||
result = new TargetSwitch(converter.convert(switchStmt.getSwitch()), cases, default_ , converter.convert(switchStmt.getType()), !switchStmt.getStatement());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -374,8 +380,6 @@ public class StatementToTargetExpression implements StatementVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(GuardedPattern aGuardedPattern) {
|
||||
//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.SimplePattern(converter.convert(aGuardedPattern.getType()), aGuardedPattern.getName()), converter.convert(aGuardedPattern.getConditions().get(0)));
|
||||
result = new TargetSwitch.Guard((TargetSwitch.Pattern) converter.convert(aGuardedPattern.getNestedPattern()), converter.convert(aGuardedPattern.getCondition()));
|
||||
}
|
||||
}
|
||||
|
@ -4,26 +4,33 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, TargetType type, boolean isExpression) implements TargetExpression {
|
||||
public record TargetSwitch(TargetExpression expr, List<Case> cases, Case default_, TargetType type, boolean isExpression) implements TargetExpression {
|
||||
|
||||
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_) {
|
||||
public TargetSwitch(TargetExpression expr, List<Case> cases, Case default_) {
|
||||
this(expr, cases, default_, null, false);
|
||||
}
|
||||
|
||||
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, TargetType type) {
|
||||
public TargetSwitch(TargetExpression expr, List<Case> cases, Case default_, TargetType type) {
|
||||
this(expr, cases, default_, type, true);
|
||||
}
|
||||
|
||||
public TargetSwitch(TargetExpression expr, List<Case> cases, TargetBlock default_, boolean isExpression) {
|
||||
public TargetSwitch(TargetExpression expr, List<Case> cases, Case default_, boolean isExpression) {
|
||||
this(expr, cases, default_, null, isExpression);
|
||||
}
|
||||
|
||||
public record Case(List<TargetExpression> labels, TargetBlock body) {}
|
||||
public record Case(List<TargetExpression> labels, TargetBlock body, boolean isSingleExpression) {
|
||||
public Case(List<TargetExpression> labels, TargetBlock body) {
|
||||
this(labels, body, false);
|
||||
}
|
||||
public Case(TargetBlock body, boolean isSingleExpression) {
|
||||
this(List.of(), body, isSingleExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed interface Pattern extends TargetExpression {}
|
||||
|
||||
public record SimplePattern(TargetType type, String name) implements Pattern {}
|
||||
public record ComplexPattern(TargetType type, List<Pattern> subPatterns) implements Pattern {}
|
||||
|
||||
public record Guard(TargetExpression inner, TargetExpression expression) implements Pattern {}
|
||||
public record Guard(Pattern inner, TargetExpression expression) implements Pattern {}
|
||||
}
|
||||
|
@ -200,46 +200,58 @@ public class TestCodegen {
|
||||
|
||||
@Test
|
||||
public void testClassicSwitch() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , "Switch");
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC , "SwitchClassic");
|
||||
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(15), 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(
|
||||
), new TargetSwitch.Case(new TargetBlock(
|
||||
List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "res"), new TargetLiteral.IntLiteral(2)), new TargetBreak())
|
||||
)),
|
||||
), false)),
|
||||
new TargetReturn(new TargetLocalVar(TargetType.Integer, "res"))
|
||||
)));
|
||||
|
||||
var clazz = generateClass(targetClass, new ByteArrayClassLoader());
|
||||
var m = clazz.getDeclaredMethod("switchClassic", Integer.class);
|
||||
assertEquals(m.invoke(null, 10), 0);
|
||||
assertEquals(m.invoke(null, 15), 1);
|
||||
assertEquals(m.invoke(null, 20), 1);
|
||||
assertEquals(m.invoke(null, 30), 1);
|
||||
assertEquals(m.invoke(null, 99), 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeSwitch() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Switch");
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "SwitchEnhanced");
|
||||
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.SimplePattern(TargetType.String, "aString")), new TargetBlock(
|
||||
List.of(new TargetLiteral.IntLiteral(0))
|
||||
)),
|
||||
List.of(new TargetYield(new TargetLiteral.IntLiteral(0)))
|
||||
), false),
|
||||
new TargetSwitch.Case(List.of(
|
||||
new TargetSwitch.Guard(new TargetSwitch.SimplePattern(TargetType.Integer, "i"), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)))
|
||||
), new TargetBlock(
|
||||
List.of(new TargetLiteral.IntLiteral(3))
|
||||
), true),
|
||||
new TargetSwitch.Case(List.of(new TargetSwitch.SimplePattern(TargetType.Integer, "anInteger")), new TargetBlock(
|
||||
List.of(new TargetLiteral.IntLiteral(1))
|
||||
))
|
||||
), new TargetBlock(
|
||||
), true)
|
||||
), new TargetSwitch.Case(new TargetBlock(
|
||||
List.of(new TargetLiteral.IntLiteral(2))
|
||||
), TargetType.Integer)
|
||||
), true), 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);
|
||||
assertEquals(m.invoke(null, 5), 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user