美文网首页
手拉手教你实现一门编程语言 Enkel, 系列 13

手拉手教你实现一门编程语言 Enkel, 系列 13

作者: KevinOfNeu | 来源:发表于2018-09-08 01:33 被阅读0次

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

    源码

    Github

    范围 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 图形化的解析树如下所示:

    image

    匹配 Antlr 上下文对象

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

    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);
            }
        }
    }
    

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

    var iterator = 0
    for (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) 为例,我们来看一下整个流程:

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

    相关文章

      网友评论

          本文标题:手拉手教你实现一门编程语言 Enkel, 系列 13

          本文链接:https://www.haomeiwen.com/subject/whfkgftx.html