超微编译器

事物的特殊性往往也能反映事物的普遍性。-- 网摘

背景:在了解抽象语法树的时候偶然了解到 the-super-tiny-compiler这个开源项目,一看之下似乎不复杂,原文全是英文,阅读的时候就想到翻译一下,一方面加深理解,一方面也作为阅读的成果。
本文主要是对源码中注释的翻译。

大多数编译器分为三个主要阶段

  1. 解析
  2. 转换
  3. 生成代码

一、解析
定义:将原始代码转成更抽象的表示。
详细解释:解析通常分为词法分析句法分析两个阶段。
词法分析:由词法分析器将原始代码分解成不同的标记,这些标记包括数字、标签、标点符号、运算符等等,这取决于你怎么去定义词法分析器。
句法分析:将词法分析器产出的标记重新格式化为一种表示形式,该表示形式描述了语法的每个部分以及它们之间的关系。 这被称为中间表示或抽象语法树。抽象语法树,简称AST,是一个深层嵌套的对象,以易于使用的方式表示代码,并告诉我们很多信息。

举例:
类LISP语法:(add 2 (subtract 4 2))
词法解析后可能是这样:

[
    { type: 'paren',  value: '('        },
    { type: 'name',   value: 'add'      },
    { type: 'number', value: '2'        },
    { type: 'paren',  value: '('        },
    { type: 'name',   value: 'subtract' },
    { type: 'number', value: '4'        },
    { type: 'number', value: '2'        },
    { type: 'paren',  value: ')'        },
    { type: 'paren',  value: ')'        },
]

句法解析后可能是这样:

{
    type: 'Program',
    body: [{
      type: 'CallExpression',
      name: 'add',
      params: [{
        type: 'NumberLiteral',
        value: '2',
      }, 
	  {
        type: 'CallExpression',
        name: 'subtract',
        params: [{
          type: 'NumberLiteral',
          value: '4',
        }, {
           type: 'NumberLiteral',
          value: '2',
        }]
      }]
    }]
}

二、转换
定义:利用代码更抽象的表示法添加编译器想做的任何操作。
详细解释:
1、变动:对拿到的AST做一些需要的改变,AST由很多语法节点组成,我们可以增加、删除、替换、更新树上的节点,当我们最终的目标语言是另一种语言时,也可以基于现有的语法树创建新的语法树。
2、遍历:采用深度优先遍历AST来获取节点。
比如有如下AST:

{
    type: 'Program',
    body: [{
      type: 'CallExpression',
      name: 'add',
      params: [{
        type: 'NumberLiteral',
        value: '2'
      }, {
        type: 'CallExpression',
        name: 'subtract',
        params: [{
          type: 'NumberLiteral',
          value: '4'
        }, {
          type: 'NumberLiteral',
          value: '2'
        }]
      }]
    }]
}

遍历顺序:

  1. Program
  2. CallExpression (add)
  3. NumberLiteral (2)
  4. CallExpression (subtract)
  5. NumberLiteral (4)
  6. NumberLiteral (2)

3、访问器:用于操作AST的节点
访问器模型:

 /*  
  *   -> Program (enter)
 *     -> CallExpression (enter)
 *       -> Number Literal (enter)
 *       <- Number Literal (exit)
 *       -> Call Expression (enter)
 *          -> Number Literal (enter)
 *          <- Number Literal (exit)
 *          -> Number Literal (enter)
 *          <- Number Literal (exit)
 *       <- CallExpression (exit)
 *     <- CallExpression (exit)
 *   <- Program (exit)
 */
 
var visitor = {
  NumberLiteral: {
    enter(node, parent) {},
    exit(node, parent) {},
  }
};

三、生成代码
定义:将转换后的代码表示转化成新的源代码。
详细解释:实际上,代码生成器要知道怎么去输出AST中所有不同类型的节点,它要递归地调用自身去输出嵌套的节点,直到所有节点都被输出到一个长的存储代码的字符串中。

小结
以上就是一个编译器不同的部分。当然,也不是说所有的编译器就是上面描述的那样,基于不同的目的,编译器可能会有更多的步骤。

源码

  1. 编译器-词法分析器
  2. 编译器-解析器
  3. 编译器-遍历器
  4. 编译器-转换器
  5. 编译器-代码生成器
  6. 完整代码

你可能感兴趣的:(js编程,编译器,源码)