babel源码分析之一:AST生成

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

内容简介: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

(完)


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

查看所有标签

猜你喜欢:

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

Agile Web Development with Rails 4

Agile Web Development with Rails 4

Sam Ruby、Dave Thomas、David Heinemeier Hansson / Pragmatic Bookshelf / 2013-10-11 / USD 43.95

Ruby on Rails helps you produce high-quality, beautiful-looking web applications quickly. You concentrate on creating the application, and Rails takes care of the details. Tens of thousands of deve......一起来看看 《Agile Web Development with Rails 4》 这本书的介绍吧!

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

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具