先了解一下基础的编译理论
首先编译器由词法分析器、语法分析器、语义分析器、中间代码生成、目标代码生成几个部分组成
词法分析器:
将代码转换为一系列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