本文系 Creating JVM language 翻译的第 13 篇。
原文中的代码和原文有不一致的地方均在新的代码仓库中更正过,建议参考新的代码仓库。

源码

Github

范围 for 循环

本节中我们来实现范围循环,在范围内迭代值,在 Java 中大概张这个样子:
for (int i=0;i<=5;i++)

Enkel 的等价形式:
for i from 0 to 5

我实现另外一个特性,循环会自动检测是递增还是递减:

1
2
3
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--)

递增或者递减必须在运行时推断,因为范围的值可能是方法调用的返回值。

for while 循环或者容器迭代器都很相似,本节不做描述。

语法规则更改

1
2
3
4
5
6
statement : block
//other statement alternatives
| forStatement ;

forStatement : 'for' ('(')? forConditions (')')? statement ;
forConditions : iterator=varReference 'from' startExpr=expression range='to' endExpr=expression ;
  • forConditions 是迭代的条件表达式
  • = 提高可读性
  • 迭代器必须是变量的名字
  • startExpression 用来初始化迭代器
  • endExpressions 是迭代器的终止值

for (i from 0 to 5) print i 图形化的解析树如下所示:

匹配 Antlr 上下文对象

Antlr 根据语法规则会生成 ForStatementContext 对象,我们用它生成对编译器更加友好的类。可以解决迭代器变量未生明的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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);
return new RangedForStatement(iteratorVariable, startExpression, endExpression,statement, varName,scope);
}
}
}

迭代器变量可能在作用中存在或者未生明,这两种情况都需要被妥善处理:

1
2
var iterator = 0
for (iterator from 0 to 5) print iterator

迭代器已经声明过,赋值给 startExpression。
new AssignmentStatement(varName,startExpression);

1
for (iterator from 0 to 5) print iterator

迭代器没有声明,首先声明,然后赋值给 startExpression。
new VariableDeclarationStatement(varName,startExpression);

字节码生成

RangedForStatement 生成后,下面我们开始生成字节码。

JVM 中没有为 for 循环设计特殊的指令。一种实现方式就是使用控制流指令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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);
}

这看起来有点复杂,因为递增递减的推测逻辑是在运行时决定的。

for (i from 0 to 5) 为例,我们来看一下整个流程:

  1. 声明迭代器变量 i 并且赋予初始值 0
  2. 检测迭代器的值 0 是否大于结束值 5
  3. 因为 0 < 5, 因此递增,跳到递增部分
  4. 执行 for 循环体内的语句
  5. 递增 1
  6. 检查迭代器的值是否大于 5
  7. 如果条件不成立,跳到 4
  8. 循环体执行 5 次后,跳到结束部分

示例

如下 Enkel 代码:

1
2
3
4
5
6
7
Loops {
main(string[] args) {
for i from 1 to 5 {
print i
}
}
}

生成后的字节码反编译后的 Java 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}

}
}

运行结果:

1
2
3
4
5
6
$ java Loops 
1
2
3
4
5