package me.august.lumen.compile.parser.ast.stmt; import me.august.lumen.compile.analyze.ASTVisitor; import me.august.lumen.compile.analyze.VisitorConsumer; import me.august.lumen.compile.codegen.Branch; import me.august.lumen.compile.codegen.BuildContext; import me.august.lumen.compile.codegen.Conditional; import me.august.lumen.compile.codegen.MethodCodeGen; import me.august.lumen.compile.parser.ast.CodeBlock; import me.august.lumen.compile.parser.ast.expr.Expression; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; public class WhileStmt implements CodeBlock, VisitorConsumer, Loop { private Expression condition; private Body body; private Label repeat; private Label exit; public WhileStmt(Expression condition, Body body) { this.condition = condition; this.body = body; } @Override public Label getRepeatLabel() { return repeat; } @Override public Label getExitLabel() { return exit; } @Override public void accept(ASTVisitor astVisitor) { astVisitor.visitWhileStmt(this); condition.accept(astVisitor); body.accept(astVisitor); } private Branch toBranch() { // if the condition is already a Conditional, we have // to modify the condition and if-body code generation // slightly to fit the structure of a while-loop. if (condition instanceof Conditional) { Conditional cond = (Conditional) condition; Branch branch = cond.branch(body, null); repeat = new Label(); exit = branch.getElseJump(); // hijack the condition to add "reset" label MethodCodeGen prevCond = branch.getCond(); branch.setCond((m, c) -> { m.visitLabel(repeat); prevCond.generate(m, c); }); // hijack the body to include a GOTO MethodCodeGen prevIfBranch = branch.getIfBranch(); branch.setIfBranch((m, c) -> { prevIfBranch.generate(m, c); m.visitJumpInsn(Opcodes.GOTO, repeat); }); return branch; // if the condition is not a Conditional, but still a boolean, // compare the condition to 0 (false), and jump to the top // (at the end of the body). } else if (condition.expressionType().getSort() == Type.BOOLEAN) { repeat = new Label(); exit = new Label(); MethodCodeGen cond = (m, c) -> { m.visitLabel(repeat); condition.generate(m, c); m.visitJumpInsn(Opcodes.IFEQ, exit); }; MethodCodeGen branchBody = (m, c) -> { body.generate(m, c); m.visitJumpInsn(Opcodes.GOTO, repeat); }; return new Branch(cond, exit, branchBody, null); } else { throw new IllegalArgumentException("Incompatible types."); } } @Override public void generate(MethodVisitor visitor, BuildContext context) { toBranch().generate(visitor, context); } @Override public String toString() { return "WhileStatement{" + "condition=" + condition + ", body=" + body + '}'; } }