本文系 翻译的第 13 篇。 原文中的代码和原文有不一致的地方均在新的中更正过,建议参考新的代码仓库。
源码
范围 for 循环
本节中我们来实现范围循环,在范围内迭代值,在 Java 中大概张这个样子: for (int i=0;i<=5;i++)
Enkel 的等价形式: for i from 0 to 5
我实现另外一个特性,循环会自动检测是递增还是递减:
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 循环或者容器迭代器都很相似,本节不做描述。
语法规则更改
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 对象,我们用它生成对编译器更加友好的类。可以解决迭代器变量未生明的问题。
public class ForStatementVisitor extends EnkelBaseVisitor{ //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); } }}复制代码
迭代器变量可能在作用中存在或者未生明,这两种情况都需要被妥善处理:
var iterator = 0for (iterator from 0 to 5) print iterator复制代码
迭代器已经声明过,赋值给 startExpression。 new AssignmentStatement(varName,startExpression);
for (iterator from 0 to 5) print iterator复制代码
迭代器没有声明,首先声明,然后赋值给 startExpression。 new VariableDeclarationStatement(varName,startExpression);
字节码生成
RangedForStatement 生成后,下面我们开始生成字节码。
JVM 中没有为 for 循环设计特殊的指令。一种实现方式就是使用控制流指令。
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)
为例,我们来看一下整个流程:
- 声明迭代器变量 i 并且赋予初始值 0
- 检测迭代器的值 0 是否大于结束值 5
- 因为 0 < 5, 因此递增,跳到递增部分
- 执行 for 循环体内的语句
- 递增 1
- 检查迭代器的值是否大于 5
- 如果条件不成立,跳到 4
- 循环体执行 5 次后,跳到结束部分
示例
如下 Enkel 代码:
Loops { main(string[] args) { for i from 1 to 5 { print i } }}复制代码
生成后的字节码反编译后的 Java 代码:
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); } }}复制代码
运行结果:
$ java Loops 12345复制代码