当前位置: 首页 > news >正文

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

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

匹配 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
复制代码

转载于:https://juejin.im/post/5b914c23e51d450e8825dc89

相关文章:

  • css过渡,css动画,页面布局分析,表单元素
  • JSONModel使用
  • 9月10日科技联播:马云将回归教育事业,张勇接任阿里巴巴董事局主席
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • Docker删除镜像
  • mysql 查询当天、本周,本月,上一个月的数据
  • java8之后的时间api
  • 站内全文搜索
  • 登录功能测试点
  • vue项目ide(vue项目环境搭建)
  • 逆地址解析协议RARP
  • linux之history命令
  • 阿里、美团、网易、华为等十余厂秋招Java面经大合集
  • flexible.js 兼容bug修复
  • 2018.09.17python学习第五天part3
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • idea + plantuml 画流程图
  • JavaScript DOM 10 - 滚动
  • javascript 哈希表
  • JavaScript 基本功--面试宝典
  • JAVA之继承和多态
  • js继承的实现方法
  • js学习笔记
  • Material Design
  • Mybatis初体验
  • MySQL主从复制读写分离及奇怪的问题
  • React+TypeScript入门
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Twitter赢在开放,三年创造奇迹
  • Unix命令
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 基于组件的设计工作流与界面抽象
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 深度学习入门:10门免费线上课程推荐
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​VRRP 虚拟路由冗余协议(华为)
  • #14vue3生成表单并跳转到外部地址的方式
  • #pragma预处理命令
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (42)STM32——LCD显示屏实验笔记
  • (6)设计一个TimeMap
  • (C语言)球球大作战
  • (libusb) usb口自动刷新
  • (pojstep1.1.2)2654(直叙式模拟)
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (转)Unity3DUnity3D在android下调试
  • (转载)OpenStack Hacker养成指南
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET Core中的去虚
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON