This article is the 13th translation of Creating JVM Language. The inconsistency between the original code and the original code has been corrected in the new code repository. We suggest you refer to the new code repository.

The source code

Github

Range for loop

In this section we implement a range loop, iterating over values in a range, which in Java looks something like this: for (int I =0; i<=5; i++)

Equivalent form of Enkel: for I from 0 to 5

I implemented another feature where the loop automatically detects whether it is increasing or decreasing:

for i from 0 to 5 //increment i from 0 to 5  - for(int i=0; i<=5; i++)for i from 5 to 0 //decremenet i from 5 to 0 - for(int i=5; i>=0; i--)Copy the code

Incrementing or decrementing must be inferred at run time because the value of the range may be the return value of the method call.

For while loops or container iterators are similar and are not described in this section.

Syntax rule changes

statement : block
           //other statement alternatives
           | forStatement ;

forStatement : 'for' ('(')? forConditions (') ')? statement ;
forConditions : iterator=varReference  'from' startExpr=expression range='to' endExpr=expression ;
Copy the code
  • ForConditions is an iterative condition expression
  • = Improved readability
  • Iterators must be variable names
  • StartExpression is used to initialize the iterator
  • EndExpressions are the end values of the iterators

For (I from 0 to 5) print I

Matches the Antlr context object

Antlr generates the ForStatementContext object based on syntactic rules, which we use to generate more compiler-friendly classes. Can solve the iterator variable unknown problem.

public class ForStatementVisitor extends EnkelBaseVisitor<RangedForStatement> {

    //other stuff
    
    @Override
    public RangedForStatement visitForStatement(@NotNull ForStatementContext ctx) {
        EnkelParser.ForConditionsContext forExpressionContext = ctx.forConditions();
        Expression startExpression = forExpressionContext.startExpr.accept(expressionVisitor);
        Expression endExpression = forExpressionContext.endExpr.accept(expressionVisitor);
        VarReferenceContext iterator = forExpressionContext.iterator;
        String varName = iterator.getText();
        //If variable referenced by iterator already exists in the scope
        if(scope.localVariableExists(varName)) { 
            //register new variable value
            Statement iteratorVariable = new AssignmentStatement(varName, startExpression); 
            //get the statement (usually block))
            Statement statement = ctx.statement().accept(statementVisitor); 
            return new RangedForStatement(iteratorVariable, startExpression, endExpression,statement, varName, scope); 
        //Variable has not been declared in the scope
        } else { 
            //create new local variable and add to the scope
            scope.addLocalVariable(new LocalVariable(varName,startExpression.getType())); 
            //register variable declaration statement
            Statement iteratorVariable = new VariableDeclarationStatement(varName,startExpression); 
            Statement statement = ctx.statement().accept(statementVisitor);
            returnnew RangedForStatement(iteratorVariable, startExpression, endExpression,statement, varName,scope); }}}Copy the code

Iterator variables may be present in action or unknown, both of which need to be handled properly:

var iterator = 0
for (iterator from 0 to 5) print iterator
Copy the code

The iterator is already declared and assigned to startExpression. new AssignmentStatement(varName,startExpression);

    for (iterator from 0 to 5) print iterator
Copy the code

Iterators are not declared; they are first declared and then assigned to startExpression. new VariableDeclarationStatement(varName,startExpression);

Bytecode generation

With the RangedForStatement generated, let’s start generating bytecode.

There are no special instructions designed for the for loop in the JVM. One way to do this is to use control flow instructions.

public void generate(RangedForStatement rangedForStatement) {
    Scope newScope = rangedForStatement.getScope();
    StatementGenerator scopeGeneratorWithNewScope = new StatementGenerator(methodVisitor, newScope);
    ExpressionGenrator exprGeneratorWithNewScope = new ExpressionGenrator(methodVisitor, newScope);
    Statement iterator = rangedForStatement.getIteratorVariableStatement();
    Label incrementationSection = new Label();
    Label decrementationSection = new Label();
    Label endLoopSection = new Label();
    String iteratorVarName = rangedForStatement.getIteratorVarName();
    Expression endExpression = rangedForStatement.getEndExpression();
    Expression iteratorVariable = new VarReference(iteratorVarName, rangedForStatement.getType());
    ConditionalExpression iteratorGreaterThanEndConditional = new ConditionalExpression(iteratorVariable, endExpression, CompareSign.GREATER);
    ConditionalExpression iteratorLessThanEndConditional = new ConditionalExpression(iteratorVariable, endExpression, CompareSign.LESS);

    //generates varaible declaration or variable reference (istore)
    iterator.accept(scopeGeneratorWithNewScope);

    //Section below checks whether the loop should be iterating or decrementing
    //If the range start is smaller than range end (i from 0 to 5)  then iterate (++)
    //If the range start is greater than range end (i from 5 to 0) then decrement (--)

    //Pushes 0 or 1 onto the stack 
    iteratorLessThanEndConditional.accept(exprGeneratorWithNewScope);
    //IFNE - is value on the stack (result of conditional) different than 0 (success)?
    methodVisitor.visitJumpInsn(Opcodes.IFNE,incrementationSection);

    iteratorGreaterThanEndConditional.accept(exprGeneratorWithNewScope);
    methodVisitor.visitJumpInsn(Opcodes.IFNE,decrementationSection);

    //Incrementation section
    methodVisitor.visitLabel(incrementationSection);
    rangedForStatement.getStatement().accept(scopeGeneratorWithNewScope); //execute the body
    methodVisitor.visitIincInsn(newScope.getLocalVariableIndex(iteratorVarName),1); //increment iterator
    iteratorGreaterThanEndConditional.accept(exprGeneratorWithNewScope); //is iterator greater than range end?
    methodVisitor.visitJumpInsn(Opcodes.IFEQ,incrementationSection); //if it is not go back loop again 
    //the iterator is greater than end range. Break out of the loop, skipping decrementation section
    methodVisitor.visitJumpInsn(Opcodes.GOTO,endLoopSection); 

    //Decrementation section
    methodVisitor.visitLabel(decrementationSection);
    rangedForStatement.getStatement().accept(scopeGeneratorWithNewScope);
    methodVisitor.visitIincInsn(newScope.getLocalVariableIndex(iteratorVarName),-1); //decrement iterator
    iteratorLessThanEndConditional.accept(exprGeneratorWithNewScope);
    methodVisitor.visitJumpInsn(Opcodes.IFEQ,decrementationSection);

    methodVisitor.visitLabel(endLoopSection);
}
Copy the code

This seems a bit complicated, because the logic of incrementing and decaying is determined at run time.

Taking for (I from 0 to 5) as an example, let’s look at the process:

  1. Declare the iterator variable I and assign an initial value of 0
  2. Checks if the iterator value 0 is greater than the end value 5
  3. Because 0 is less than 5, it increases, so it jumps to the increasing part
  4. Execute the statement inside the for loop
  5. Increasing 1
  6. Check if the iterator value is greater than 5
  7. If the condition is not true, jump to 4
  8. After executing the loop body five times, skip to the end

The sample

Enkel code:

Loops {
    main(string[] args) {
        for i from 1 to 5 {
            print i
        }
    }
}
Copy the code

Java code decompiled from generated bytecode:

public class Loops {
    public static void main(String[] var0) {
        int var1 = 1;
        if(var1 >= 5 ) { //should it be decremented?
            do {
                System.out.println(var1);
                --var1;
            } while(var1 >= 5);
        } else { //should it be incremented?
            do {
                System.out.println(var1);
                ++var1;
            } while(var1 <= 5); }}}Copy the code

Running results:

$ java Loops 
1
2
3
4
5
Copy the code