Quiet
  • HOME
  • ARCHIVE
  • CATEGORIES
  • TAGS
  • LINKS
  • ABOUT

李致知

  • HOME
  • ARCHIVE
  • CATEGORIES
  • TAGS
  • LINKS
  • ABOUT
Quiet主题
  • JS

从零实现一个JS编译器

李致知
技术点

2023-03-28 12:33:23

文章目录
  1. 一,背景
  2. 二,笔记

一,背景

实现过程是学习这个网站的笔记:从零实现一个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');
  }
}
上一篇

一句话提醒自己列表

下一篇

LaTeX入门

©2023 By 李致知. 主题: Quiet 鲁ICP备2022039519号-1
Quiet主题