Add ForEach loop

This commit is contained in:
Daniel Holle 2024-01-19 16:29:59 +01:00
parent 6cc40162da
commit b80cc726c8
12 changed files with 137 additions and 6 deletions

View File

@ -0,0 +1,15 @@
import java.util.ArrayList;
import java.lang.Integer;
public class ForEach {
m() {
var list = new ArrayList<>();
list.add(1); list.add(2); list.add(3);
var sum = 0;
for (var i : list) {
sum = sum + i;
}
return sum;
}
}

View File

@ -2,6 +2,7 @@ package de.dhbwstuttgart.bytecode;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.type.RefType;
@ -918,6 +919,9 @@ public class Codegen {
state.localCounter = localCounter;
break;
}
case TargetForEach forEach:
generateForEach(forEach, state);
break;
case TargetWhile _while: {
Label start = new Label();
Label end = new Label();
@ -1041,6 +1045,35 @@ public class Codegen {
}
}
private void generateForEach(TargetForEach forEach, State state) {
state.enterScope();
TargetVarDecl vd = (TargetVarDecl) forEach.vardecl();
var localVar = state.createVariable(vd.name(), vd.varType());
var expr = forEach.expression();
// TODO Check for arrays
var mv = state.mv;
generate(state, expr);
mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Iterable", "iterator", "()Ljava/util/Iterator;", true);
var start = new Label();
var end = new Label();
mv.visitLabel(start);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true);
mv.visitJumpInsn(IFEQ, end);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true);
mv.visitVarInsn(ASTORE, localVar.index);
generate(state, forEach.body());
mv.visitJumpInsn(GOTO, start);
mv.visitLabel(end);
mv.visitInsn(POP);
state.exitScope();
}
private void generateInstanceOf(State state, TargetInstanceOf instanceOf) {
var mv = state.mv;

View File

@ -490,15 +490,32 @@ public class StatementGenerator {
private Statement convert(Java17Parser.ForloopContext stmt) {
var control = stmt.forControl();
var block = convert(stmt.statement());
if (control.enhancedForControl() != null)
throw new NotImplementedException();
else {
if (control.enhancedForControl() != null) {
var forCtrl = control.enhancedForControl();
var name = forCtrl.variableDeclaratorId().identifier();
RefTypeOrTPHOrWildcardOrGeneric type;
if (Objects.isNull(forCtrl.typeType()) || !Objects.isNull(forCtrl.VAR())) {
type = TypePlaceholder.fresh(forCtrl.getStart());
} else {
type = TypeGenerator.convert(forCtrl.typeType(), reg, generics);
}
var vardecl = new LocalVarDecl(name.getText(), type, name.getStart());
this.localVars.put(name.getText(), type);
return new ForEachStmt(
stmt.getStart(),
vardecl,
convert(forCtrl.expression()),
convert(stmt.statement())
);
} else {
return new ForStmt(
stmt.getStart(),
convert(control.forInit().localVariableDeclaration()),
convert(control.expression()), control.forUpdate.expression().stream().map(this::convert).toList(),
block
convert(stmt.statement())
);
}
}

View File

@ -166,6 +166,11 @@ public abstract class AbstractASTWalker implements ASTVisitor {
forStmt.block.accept(this);
}
@Override
public void visit(ForEachStmt forEachStmt) {
forEachStmt.block.accept(this);
}
@Override
public void visit(IfStmt ifStmt) {
ifStmt.then_block.accept(this);

View File

@ -25,6 +25,8 @@ public interface StatementVisitor {
void visit(ForStmt forStmt);
void visit(ForEachStmt forEachStmt);
void visit(IfStmt ifStmt);
void visit(InstanceOf instanceOf);

View File

@ -0,0 +1,26 @@
package de.dhbwstuttgart.syntaxtree.statement;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.syntaxtree.StatementVisitor;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.Void;
import org.antlr.v4.runtime.Token;
public class ForEachStmt extends Statement {
public final Statement statement;
public final Expression expression;
public final Statement block;
public ForEachStmt(Token offset, Statement stmt, Expression expression, Statement block) {
super(new Void(new NullToken()), offset);
this.statement = stmt;
this.expression = expression;
this.block = block;
}
@Override
public void accept(StatementVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -244,6 +244,11 @@ public class OutputGenerator implements ASTVisitor {
}
@Override
public void visit(ForEachStmt forEachStmt) {
}
@Override
public void visit(IfStmt ifStmt) {
out.append("if(");

View File

@ -144,6 +144,11 @@ public class StatementToTargetExpression implements ASTVisitor {
result = new TargetFor(forStmt.initializer.stream().map(converter::convert).toList(), converter.convert(forStmt.condition), forStmt.loopExpr.stream().map(converter::convert).toList(), converter.convert(forStmt.block));
}
@Override
public void visit(ForEachStmt forEachStmt) {
result = new TargetForEach(converter.convert(forEachStmt.statement), converter.convert(forEachStmt.expression), converter.convert(forEachStmt.block));
}
@Override
public void visit(IfStmt ifStmt) {
result = new TargetIf(converter.convert(ifStmt.expr), converter.convert(ifStmt.then_block), ifStmt.else_block != null ? converter.convert(ifStmt.else_block) : null);

View File

@ -66,6 +66,11 @@ public abstract class TracingStatementVisitor implements StatementVisitor {
forStmt.block.accept(this);
}
@Override
public void visit(ForEachStmt forEachStmt) {
forEachStmt.block.accept(this);
}
@Override
public void visit(IfStmt ifStmt) {
ifStmt.then_block.accept(this);

View File

@ -1,4 +1,4 @@
package de.dhbwstuttgart.target.tree.expression;
public record TargetForEach(TargetExpression vardecl, TargetExpression list) implements TargetExpression {
public record TargetForEach(TargetExpression vardecl, TargetExpression expression, TargetExpression body) implements TargetExpression {
}

View File

@ -128,6 +128,15 @@ public class TYPEStmt implements StatementVisitor {
forStmt.block.accept(this);
}
@Override
public void visit(ForEachStmt forEachStmt) {
var iterableType = new RefType(ASTFactory.createClass(java.lang.Iterable.class).getClassName(), Arrays.asList(forEachStmt.statement.getType()), new NullToken());
constraintsSet.addUndConstraint(new Pair(forEachStmt.expression.getType(), iterableType, PairOperator.SMALLERDOT));
forEachStmt.statement.accept(this);
forEachStmt.expression.accept(this);
forEachStmt.block.accept(this);
}
@Override
public void visit(IfStmt ifStmt) {
RefType booleanType = new RefType(ASTFactory.createClass(java.lang.Boolean.class).getClassName(), new NullToken());

View File

@ -738,6 +738,15 @@ public class TestComplete {
assertEquals(m.invoke(instance, 10), 60);
}
@Test
public void testForEach() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "ForEach.jav");
var clazz = classFiles.get("ForEach");
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("m");
assertEquals(m.invoke(instance), 6);
}
@Test
public void testLambdaRunnable() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "LambdaRunnable.jav");