babel源码分析之一:AST生成

栏目: JavaScript · 发布时间: 5年前

内容简介:babel是javaScript编译器,主要用于将ECMAScript2015+版本的代码转化为具有兼容性的较低版本,从而让代码适用于各种环境。它的早期代码从acorn项目中fork出来,后续提供了acorn不具备的一整套的代码解析,转换,生成的功能。babel代码转换第一步是将源码转化成AST(Abstract Syntax Tree)。

前言

babel是javaScript编译器,主要用于将ECMAScript2015+版本的代码转化为具有兼容性的较低版本,从而让代码适用于各种环境。

它的早期代码从acorn项目中fork出来,后续提供了acorn不具备的一整套的代码解析,转换,生成的功能。

babel代码转换第一步是将源码转化成AST(Abstract Syntax Tree)。

本文将借助acorn初期的源码以及 ESTree 规范 标准,详细讲解其生成AST的工程细节。

功能分析

先举个代码转为ast的例子:

/*
   测试whileStatement
*/
while(b !== 0){
  if (a > b){
   a = a - b;
  }else{
   b = b - a;
  }
}
console.log(a)

转化后的ast结构

babel源码分析之一:AST生成

实现思路

上图的整个树的生成都是由一次次词法,语法解析中递归出来的。一个完整的递归函数逻辑如下:

  • 去除注释,空格,换行。
  • 词法分析: 将源码中的字符转化为单词。即解析token,如while(b !== 0){将被识别为的[while,(,b,!==,0,),{]这7个单词。
  • 语法分析:通过词法解析出来的单词token来获取statement节点的类型并解析,然后对其中可能含有的expression进行相应的语法解析。解析出其开始的start,结束的end,值value以及值的类型label。
  • 索引移到下一位,开启新一轮的递归。以此循环直到将文件字符串读取完毕。
...
  while (tokType !== _eof) {
        const statement = parseStatement();
        if (first) {
            first = false;
            if (isUseStrict(statement)) {
                setStrict(true);
            }
        }
        node.body.push(statement);
    }
 ...

功能实现

单个递归函数的实现的功能如下

去除注释,空格,换行

注释分两种:

/ /,或者 / * /的多行注释以及//的单行注释。

空格分为' ' t n r f

function skipSpace() {
    while (tokenPos < inputLen) {
        const ch = input.charAt(tokenPos);
        if (ch === '/') {
            if (input.charAt(tokenPos + 1) === '/') {
                tokenPos += 2;
                while (tokenPos < inputLen && !newline.test(input.charAt(tokenPos))) {
                    tokenPos++;
                }
            } else if (input.charAt(tokenPos + 1) === '*') {
                const i = input.indexOf('*/', tokenPos + 2);
                if (i < 0) {
                    raise(tokenPos - 2, 'Unterminated comment');
                }
                tokenPos = i + 2;
            } else {
                ++tokenPos;
            }
        } else if (ch === '\n' || ch === '\t' || ch === " " || ch === "\r" || ch === "\f") {
            ++tokenPos;
        } else {
            break;
        }
    }
}

解析token

具体token有非常多,但是按类型分的话,可以分为以下6种:

  • string字符串类型。以' " 开头,且以' "结尾。
  • regexp正则类型。以/开头,且以/结尾
  • word单词类型。关键字如break case catch continue debugger。保留关键字如implements interface let package private以及普通的变量名称。
  • number数字类型。类型有:二进制,八进制,十进制和十六进制。其中0x或者0X开头的是十六进制。
  • punctuation标点符号类型。如[ { ( , ; ? 等符号。
  • operator运算符类型。如+ - * % | & = 等符号。对于不同符号在一起解析的时候,会有不同的解析优先级。

    • 优先级最高为10: - * % /
    • 优先级为9:+ -
    • 优先级为8: >> >>> << <<<
    • 优先级为7: > >= < <=
    • 优先级为6: === == !== !===
    • 优先级为5: &
    • 优先级为4: ^
    • 优先级为3: |
    • 优先级为2: &&
    • 优先级为1: ||
function readToken() {
    lastStart = tokStart;
    lastEnd = tokEnd;
    tokStart = tokenPos;
    const ch = input.charAt(tokenPos);
    if (tokenPos >= inputLen) {
        return finishToken(_eof);
    }
    if (ch === '\'' || ch === '"') {
        readString(ch);
    } else if (indentifierReg.test(ch)) {
        readWord();
    } else if (digest.test(ch)) {
        readNumber();
    } else if (puncChars.test(ch)) {
        tokenPos++;
        finishToken(puncTypes[ch]);
    } else if (operatorChar.test(ch)) {
        readOperator(ch)
    }
}

解析节点Statements

除了BlockStatement,其他statement是以;或者换行符结束。

每种statement都由自己不同的解析方式以及名称。一个statement可能含有0个或者多个expression。

如while类型的statement解析函数如下

function parseWhile() {
    const node = startNode();
    next();
    node.test = parseParenExpression();
    node.body = parseBlock();
    return finishNode(node, 'WhileStatement');
}

解析后的简单的json类型为

{
      "type": "WhileStatement",
      "test": {
        "type": "AssignmentExpression",
        "operator": "=",
        "left": {
          "type": "Identifier",
          "name": "a"
        },
        "right": {
          "type": "Identifier",
          "name": "b"
        }
      },
      "body": {
        "type": "BlockStatement",
        "body": []
      }
    }

解析expression

这个模块个人认为是最核心的模块,对不同表达式进行解析。

最基本的表达式如:Identifier,Literal,FunctionExpression,ObjectExpression,ArrayExpression,NewExpression。

建立在基本表达式之上的如:a.b的MemberExpression,a()的CallExpression。

++a,a--之类的UpdateExpression。

!a,!!a之类的UnaryExpression。

a||b,a&&b的LogicalExpression,a-b之类的BinaryExpression。

a=b之类的AssignmentExpression。

a?b:c之类的ConditionalExpression。

上面这些复杂类型的解析执行顺序如下:

babel源码分析之一:AST生成

举个复杂的例子:

var a=!b++?c+1:d.e(f,g);

解析之后的json格式如下

{
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "a"
          },
          "init": {
            "type": "ConditionalExpression",
            "test": {
              "type": "UnaryExpression",
              "operator": "!",
              "prefix": true,
              "argument": {
                "type": "UpdateExpression",
                "operator": "++",
                "prefix": false,
                "argument": {
                  "type": "Identifier",
                  "name": "b"
                }
              }
            },
            "consequent": {
              "type": "BinaryExpression",
              "left": {
                "type": "Identifier",
                "name": "c"
              },
              "operator": "+",
              "right": {
                "type": "Literal",
                "value": 1,
                "raw": "1"
              }
            },
            "alternate": {
              "type": "CallExpression",
              "callee": {
                "type": "MemberExpression",
                "object": {
                  "type": "Identifier",
                  "name": "d"
                },
                "property": {
                  "type": "Identifier",
                  "name": "e"
                },
                "computed": false
              },
              "arguments": [
                {
                  "type": "Identifier",
                  "name": "f"
                },
                {
                  "type": "Identifier",
                  "name": "g"
                }
              ]
            }
          }
        }
      ],
      "kind": "var"
    }

代码实现

本人的简易版babel实现 simple-babel

(完)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大学的终结

大学的终结

[美] 凯文·凯里(Kevin Carey) / 朱志勇、韩倩 / 人民邮电出版社 / 2017-2-28 / 59.00

你了解目前全球高等教育的现状吗?你知道高等教育的未来是什么样的吗?你听说过泛在大学吗?翻开本书,了解大学的过去、现在与未来。 《大学的终结:泛在大学与高等教育革命》一书由美国著名教育作家凯文? 凯里倾情打造。作者在书中详细论述了美国大学的历史变迁、大学的本质、大学的未来、信息技术与教育的关系、泛在大学的定义、传统大学在大趋势下的挣扎,以及未来高等教育的学历认证与呈现形式。本书作者用缜密的逻辑......一起来看看 《大学的终结》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具