一,背景
实现过程是学习这个网站的笔记:从零实现一个js编译器
这是一个github Star 两万多的一个项目,用javascript编写的简单编译器。
二,笔记
有错误,我在tranforme时失败了,咱也不知道问题出在哪,下次再说。。。
//我们的目标是将lisp语言语句转换为js语句
add(2, sub(4, 3)) // Javascript
(add 2 (sub 4 3)) // Lisp
//index.js
const compiler = require('./compiler')
const input = '(add 2 (sub 4 3))' //Lisp语言
const output = compiler(input) //JS字符串
// console.log(JSON.stringify(output, null, 2))
console.log(output)
//compiler.js
const tokenizer = require('./tokenizer')
const parser = require('./parser')
const transformer = require('./transformer')
const generateCode = require('./generateCode')
module.exports = function compiler(input) {
//1,语法分析
//将输入字符串转换成基本的语言语法(块)
const tokens = tokenizer(input)
//2,句法分析
//上一步我们只收集了所有语法片段,却忽略了他们之间的关系。
//这一阶段就是在树状结构中表示lisp代码,包含将其转换为另一种语言的所有需要信息,叫做AST语法树。
const lispAST = parser(tokens)
//3,转换
//将适合lisp的语法树变成适合javascript的语法树
const jsAST = transformer(lispAST)
//4,代码生成
const jsCode = generateCode(jsAST)
return lispAST
}
//tokenizer.js
const LETTERS = /[a-z]/i //匹配所有字母,i表示忽略大小写
const WHITESPACE = /\s/ //匹配空格
module.exports = function tokenizer(input){
const tokens = []
let current = 0
while (current < input.length){
let char = input[current]
//条件判断
if(char == '('){
tokens.push({
type:'paren',
value:'('
})
current++;
continue;
}
if(LETTERS.test(char)){
let value = ''
while (LETTERS.test(char)){
value += char;
char = input[++current]
}
tokens.push({
type:'name',
value
});
continue;
}
//如果都不满足就抛出错误
throw new TypeError(`unknown char:'${char}'`)
}
return tokens
}
//parser.js
module.exports = 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 === 'paren' && token.value === '(') {
token = tokens[++current]
const expression = {
type: ' CallExpression',
name: token.value,
params: [],
}
token = tokens[++current]
//获取参数
while (token.value !== ')') {
expression.params.push(walk())
token = tokens[current]
}
current++
return expression
}
throw new TypeError(`Unknown token:'${token.type}'`)
}
const ast = {
type: 'Program', //根节点
body: [walk()], //成员数组,每一行都是一个成员
}
return ast
}
//tranformer.js
const traverse = require('./traverse')
module.exports = function transformer(originalAST) {
const jsAST = {
type: 'Program',
body: [],
}
let position = jsAST.body
traverse(originalAST, {
NumberLiteral(node) {
position.push({
type: 'NumericLiteral',
value: node.value,
})
},
CallExpression(node, parent) {
let expression = {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: node.name, //函数的名字
},
arguments: [], //参数列表
}
const prevPosition = position
position = expression.arguments
if (parent.type !== 'CallExpression') {
expression = {
type: 'ExpressionStatement',
expression,
}
}
prevPosition.push(expression)
},
})
return jsAST
}
// traverse.js
module.exports = function traverse(ast, visitors) {
function walkNode(node, parent) {
const method = visitors[node.type]
if (method) {
method(node, parent)
}
//遍历根节点的每个子节点
if (node.type === 'Program') {
walkNodes(node.body, node)
} else if (node.type === 'CallExpression') {
//处理CallExpression的params数组节点
walkNodes(node.params, node)
}
}
function walkNodes(nodes, parent) {
nodes.forEach((node) => walkNode(node, parent))
}
//初始化第一次行走
walkNode(ast, null)
}
//generateCode.js
module.exports = function generateCode(node) {
if (node.type === 'NumericLiteral') {
return node.value;
}
if (node.type === 'Identifier') {//标识符,也就是函数的名称
return node.name;
}
if (node.type === 'CallExpression') {
return `${generateCode(node.callee)}(${node.arguments.map(generateCode).join(', ')})`;
}
if (node.type === 'ExpressionStatement') {
return `${generateCode(node.expression)};`;
}
if (node.type === 'Program') {
return node.body.map(generateCode).join('\n');
}
}