编译器 babel 的编译原理

先了解一下基础的编译理论

首先编译器由词法分析器、语法分析器、语义分析器、中间代码生成、目标代码生成几个部分组成

词法分析器:

将代码转换为一系列token,对词法分析器来说代码就是一个字符串,整个过程就是按母译语言的语法去解析字符串的过程,它可以将人们书写的代码(也就是字符串),转化成这个语言所定义的一个个基本单位,比如一个一个减法运算的主值、右值、加号等等,我们将这些基本单位称之为token。

语法分析器 和 语义分析器:

它们的产出都是AST。区别在于语法分析器是通过token生成初始的AST结构,语义分析器是遍历整个AST对其每个节点的属性进行补全,对语法分析器的结果进行增强。

中间代码生成

它往往存在于面向多个目标平台的场景中,如windows、Linux、Mac,它可以将AST生成为和平台无关的代码,只需要再单独开发每个平台的编译器后端就可以了,前端的部分可以公用,当然了如果不存在多个目标平台的情况下也可以不生成中间代码,直接将代码翻译成目标平台的代码,比如Babel就是这样的

完整编译流程 

Babel编译器

输入的是高版本的ES代码,输出的是符合我们要求的低版本ES代码,例如: ES6->ES5

Babel的工作步骤

1、解析(Parsing): 解析代码,生成AST (抽象语法树)

2、变换(Transformation): 操作AST(抽象语法树),修改其内容

3、生成 (Code Generation) :根据AST(抽象语法树)生成新的代码

如何实现一个编译器

1、Input -> Tokenizer ->  Tokens

实现一个Tokenizer函数,其实就是通过一个while循环依次的读取代码,当发现一下特殊字符的时候(比如左右括号、)就生成一个token对象push到token数组中,完成后返回全部Token。

function tokenizer(input) {
    let current = 0
    let tokens = []
    while (current < input.length) {
        let char = input[current]
        if (char === '('){ 
            tokens.push({ type: 'paren', value:'('})
        }
        if (char === ')'){
            tokens.push({type: 'paren',value:')'})
        }
        
        //对于数字、变量名、关键字等由多个字符串组成的token也可以采用更复杂的处理方式,如:
        let NUMBERS = /[0-9]/
        if (NUMBERS.test(char )) {
            let value = ''
            while (NUMBERS.test(char )) {
                value += char
                char = input[++current]
            }
            tokens.push({ type: 'number', value })
            continue;
        }

        let LETTERS = /[a-z]/i
        if (LETTERS.test(char)) {
            let value = ''
            while (LETTERS.test(char)) {
                value += char
                char = input[++current]
            }
            tokens.push({ type: 'name', value });
            continue;
        }        

        //...
        current++;
    }
    return tokens
}

2、Tokens -> Parser -> AST

实现一个Parder函数,将上一步得到的token转换成AST语法树。具体做法就是依次遍历token数组,将token根据语法上的嵌套关系组合起来,生成一个树状结构,即AST语法树。

function parser(tokens) {
    let current = 0;
    function walk() {
        let token = tokens[current];
        if (token.type === 'number') {
            current++;
            return {
                type: 'NumberLiteral', 
                value: token .value,
            }
        }
        if (token.type === 'string' ) {
            current++;
            return {
                type: 'StringLiteral', 
                value: token.value1
            }
        }
        //...
    }
    let ast = { type: 'Program', body: [], };
    while (current < tokens .length) {
        ast.body.push(walk( ));
    }
    return ast;
}

3、实现一个深度遍历的Traverser函数作为遍历的工具,

4、AST -> transformer -> NewAst

实现一个Transformer函数,用来在遍历到每个节点的时候对当前节点进行转换工作,这个过程往往不是直接改动旧语法树,而是根据目标语言的语法构造一棵新的语法树。由于涉及到很多语言的设计细节,这里就不展开说了。

5、NewAst -> CodeGenerator -> Output

最后一步实现一个CodeGenerator函数,深度优先遍历新AST语法树,将每个节点依次组合成新的代码

如果对编译原理感兴趣的可以看:中国大学MOOC(慕课)_国家精品课程在线学习平台

对AST结构感兴趣的可以看:AST Explorer 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值