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:
- Declare the iterator variable I and assign an initial value of 0
- Checks if the iterator value 0 is greater than the end value 5
- Because 0 is less than 5, it increases, so it jumps to the increasing part
- Execute the statement inside the for loop
- Increasing 1
- Check if the iterator value is greater than 5
- If the condition is not true, jump to 4
- 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