更多博客文章,欢迎 Star Github/Blog
Babel 是 JavaScript 编译器 compiler,更确切地说是源码到源码的编译器,一般也叫作 转换编译器(transpiler)
。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,而后返回给你新生成的代码。node
Babel 是一个通用的多功能的 JavaScript 编译器。此外它还拥有众多模块可用于不一样形式的静态分析。静态分析是在不须要执行代码的前提下对代码进行分析的处理过程 (执行代码的同时进行代码分析便是动态分析)。 静态分析的目的是多种多样的, 它可用于语法检查,编译,代码高亮,代码转换,优化,压缩等等场景。git
Babylon 是 Babel 的解析器 parser。最初是 从 Acorn 项目 fork 出来的。Acorn 很是快,易于使用,而且针对非标准特性(以及那些将来的标准特性) 设计了一个基于插件的架构。github
Babylon 已经移入 Babel mono-repo 改名为 babel-parser
首先,让咱们安装它。正则表达式
$ npm install --save babylon
先从解析一个代码字符串开始:shell
import * as babylon from "babylon"; const code = `function square(n) { return n * n; }`; babylon.parse(code); // Node { // type: "File", // start: 0, // end: 38, // loc: SourceLocation {...}, // program: Node {...}, // comments: [], // tokens: [...] // }
咱们还能像下面这样传递选项给 parse()
方法:express
babylon.parse(code, { sourceType: "module", // default: "script" plugins: ["jsx"] // default: [] });
sourceType
能够是 "module"
或者 "script"
,它表示 Babylon 应该用哪一种模式来解析。 "module"
将会在严格模式下解析而且容许模块定义,"script"
则不会。npm
注意:sourceType
的默认值是"script"
而且在发现import
或export
时产生错误。 使用scourceType: "module"
来避免这些错误。
因为 Babylon 使用了基于插件的架构,所以有一个 plugins
选项能够开关内置的插件。 注意 Babylon 还没有对外部插件开放此 API 接口,不排除将来会开放此API。json
解析(Parse )步骤接收代码并输出 抽象语法树(AST)
。 这个步骤分为两个阶段:词法分析(Lexical Analysis) 和 语法分析(Syntactic Analysis)。数组
词法分析阶段把字符串形式的代码转换为 令牌(tokens) 流。babel
你能够把令牌看做是一个扁平的语法片断数组:
n * n;
[ { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } }, { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } }, { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } }, ... ]
每个 type
有一组属性来描述该令牌:
{ type: { label: 'name', keyword: undefined, beforeExpr: false, startsExpr: true, rightAssociative: false, isLoop: false, isAssign: false, prefix: false, postfix: false, binop: null, updateContext: null }, ... }
和 AST 节点同样它们也有 start
,end
,loc
属性。
语法分析阶段会把一个令牌流转换成 抽象语法树(AST)
的形式。 这个阶段会使用令牌中的信息把它们转换成一个 AST 的表述结构,这样更易于后续的操做。
这个处理过程当中的每一步都涉及到建立或是操做抽象语法树,亦称 AST。
Babel 使用一个基于 ESTree 并修改过的 AST,它的内核说明文档能够在 这里. com/babel/babel/blob/master/doc/ast/spec. md)找到。
function square(n) { return n * n; }
AST Explorer 可让你对 AST 节点有一个更好的感性认识。 这里是上述代码的一个示例连接。
这个程序能够被表示成以下所示的 JavaScript Object(对象):
{ type: "FunctionDeclaration", id: { type: "Identifier", name: "square" }, params: [{ type: "Identifier", name: "n" }], body: { type: "BlockStatement", body: [{ type: "ReturnStatement", argument: { type: "BinaryExpression", operator: "*", left: { type: "Identifier", name: "n" }, right: { type: "Identifier", name: "n" } } }] } }
你会留意到 AST 的每一层都拥有相同的结构:
{ type: "FunctionDeclaration", id: {...}, params: [...], body: {...} }
{ type: "Identifier", name: ... }
{ type: "BinaryExpression", operator: ..., left: {...}, right: {...} }
注意:出于简化的目的移除了某些属性
这样的每一层结构也被叫作 节点(Node)。 一个 AST 能够由单一的节点或是成百上千个节点构成。 它们组合在一块儿能够描述用于静态分析的程序语法。
每个节点都有以下所示的接口(Interface):
interface Node { type: string; }
字符串形式的 type
字段表示节点的类型(如: "FunctionDeclaration"
,"Identifier"
,或 "BinaryExpression"
)。 每一种类型的节点定义了一些附加属性用来进一步描述该节点类型。
Babel 还为每一个节点额外生成了一些属性,用于描述该节点在原始代码中的位置。
{ type: ..., start: 0, end: 38, loc: { start: { line: 1, column: 0 }, end: { line: 3, column: 1 } }, ... }
每个节点都会有 start
,end
,loc
这几个属性。
let a = 'hello'
变量声明,kind
属性表示是什么类型的声明,由于 ES6 引入了 const/let
。declarations
表示声明的多个描述,由于咱们能够这样:let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var"; }
变量声明的描述,id
表示变量名称节点,init
表示初始值的表达式,能够为 null
。
interface VariableDeclarator <: Node { type: "VariableDeclarator"; id: Pattern; init: Expression | null; }
标识符,我以为应该是这么叫的,就是咱们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:
interface Identifier <: Expression, Pattern { type: "Identifier"; name: string; }
一个标识符多是一个表达式,或者是解构的模式(ES6 中的解构语法)。咱们等会会看到 Expression
和 Pattern
相关的内容的。
字面量,这里不是指 []
或者 {}
这些,而是自己语义就表明了一个值的字面量,如 1
,“hello”
, true
这些,还有正则表达式(有一个扩展的 Node
来表示正则表达式),如 /\d?/
。咱们看一下文档的定义:
interface Literal <: Expression { type: "Literal"; value: string | boolean | null | number | RegExp; }
value
这里即对应了字面量的值,咱们能够看出字面量值的类型,字符串,布尔,数值,null
和正则。
let a = 3+4
二元运算表达式节点,left
和 right
表示运算符左右的两个表达式,operator
表示一个二元运算符。
interface BinaryExpression <: Expression { type: "BinaryExpression"; operator: BinaryOperator; left: Expression; right: Expression; }
二元运算符,全部值以下:
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in" | "instanceof" }
if(a === 0){ }
if
语句节点,很常见,会带有三个属性,test
属性表示 if (...)
括号中的表达式。
consequent
属性是表示条件为 true
时的执行语句,一般会是一个块语句。
alternate
属性则是用来表示 else
后跟随的语句节点,一般也会是块语句,但也能够又是一个 if
语句节点,即相似这样的结构:if (a) { //... } else if (b) { // ... }
。alternate
固然也能够为 null
。
interface IfStatement <: Statement { type: "IfStatement"; test: Expression; consequent: Statement; alternate: Statement | null; }
常见的 AST node types 在 Babylon 中 定义以下:
符合规范的解析出来的 AST 节点用 Node
对象来标识,Node
对象应该符合这样的接口:
interface Node { type: string; loc: SourceLocation | null; }
type
字段表示不一样的节点类型,下边会再讲一下各个类型的状况,分别对应了 JavaScript 中的什么语法。loc
字段表示源码的位置信息,若是没有相关信息的话为 null
,不然是一个对象,包含了开始和结束的位置。接口以下:
interface SourceLocation { source: string | null; start: Position; end: Position; }
这里的 Position
对象包含了行和列的信息,行从 1 开始,列从 0 开始:
interface Position { line: number; // >= 1 column: number; // >= 0 }
标识符,就是咱们写 JS 时自定义的名称,如变量名,函数名,属性名,都归为标识符。相应的接口是这样的:
interface Identifier <: Expression, Pattern { type: "Identifier"; name: string; }
一个标识符多是一个表达式,或者是解构的模式(ES6 中的解构语法)。咱们等会会看到 Expression
和 Pattern
相关的内容的。
interface PrivateName <: Expression, Pattern { type: "PrivateName"; id: Identifier; }
A Private Name Identifier.
字面量,这里不是指 []
或者 {}
这些,而是自己语义就表明了一个值的字面量,如 1
,“hello”
, true
这些,还有正则表达式(有一个扩展的 Node
来表示正则表达式),如 /\d?/
。咱们看一下文档的定义:
interface Literal <: Expression { type: "Literal"; value: string | boolean | null | number | RegExp; }
value
这里即对应了字面量的值,咱们能够看出字面量值的类型,字符串,布尔,数值,null
和正则。
这个针对正则字面量的,为了更好地来解析正则表达式的内容,添加多一个 regex
字段,里边会包括正则自己,以及正则的 flags
。
interface RegExpLiteral <: Literal { regex: { pattern: string; flags: string; }; }
通常这个是做为根节点的,即表明了一棵完整的程序代码树。
interface Program <: Node { type: "Program"; body: [ Statement ]; }
body
属性是一个数组,包含了多个 Statement
(即语句)节点。
函数声明或者函数表达式节点。
interface Function <: Node { id: Identifier | null; params: [ Pattern ]; body: BlockStatement; }
id
是函数名,params
属性是一个数组,表示函数的参数。body
是一个块语句。
有一个值得留意的点是,你在测试过程当中,是不会找到 type: "Function"
的节点的,可是你能够找到 type: "FunctionDeclaration"
和 type: "FunctionExpression"
,由于函数要么以声明语句出现,要么以函数表达式出现,都是节点类型的组合类型,后边会再说起 FunctionDeclaration
和 FunctionExpression
的相关内容。
语句节点没什么特别的,它只是一个节点,一种区分,可是语句有不少种,下边会详述。
interface Statement <: Node { }
表达式语句节点,a = a + 1
或者 a++
里边会有一个 expression
属性指向一个表达式节点对象(后边会说起表达式)。
interface ExpressionStatement <: Statement { type: "ExpressionStatement"; expression: Expression; }
块语句节点,举个例子:if (...) { // 这里是块语句的内容 }
,块里边能够包含多个其余的语句,因此有一个 body
属性,是一个数组,表示了块里边的多个语句。
interface BlockStatement <: Statement { type: "BlockStatement"; body: [ Statement ]; }
返回语句节点,argument
属性是一个表达式,表明返回的内容。
interface ReturnStatement <: Statement { type: "ReturnStatement"; argument: Expression | null; }
if
语句节点,很常见,会带有三个属性,test
属性表示 if (...)
括号中的表达式。
consequent
属性是表示条件为 true
时的执行语句,一般会是一个块语句。
alternate
属性则是用来表示 else
后跟随的语句节点,一般也会是块语句,但也能够又是一个 if
语句节点,即相似这样的结构:if (a) { //... } else if (b) { // ... }
。alternate
固然也能够为 null
。
interface IfStatement <: Statement { type: "IfStatement"; test: Expression; consequent: Statement; alternate: Statement | null; }
switch
语句节点,有两个属性,discriminant
属性表示 switch
语句后紧随的表达式,一般会是一个变量,cases
属性是一个 case
节点的数组,用来表示各个 case
语句。
interface SwitchStatement <: Statement { type: "SwitchStatement"; discriminant: Expression; cases: [ SwitchCase ]; }
for
循环语句节点,属性 init/test/update
分别表示了 for
语句括号中的三个表达式,初始化值,循环判断条件,每次循环执行的变量更新语句(init
能够是变量声明或者表达式)。这三个属性均可觉得 null
,即 for(;;){}
。body
属性用以表示要循环执行的语句。
interface ForStatement <: Statement { type: "ForStatement"; init: VariableDeclaration | Expression | null; test: Expression | null; update: Expression | null; body: Statement; }
声明语句节点,一样也是语句,只是一个类型的细化。下边会介绍各类声明语句类型。
interface Declaration <: Statement { }
函数声明,和以前提到的 Function 不一样的是,id
不能为 null
。
interface FunctionDeclaration <: Function, Declaration { type: "FunctionDeclaration"; id: Identifier; }
变量声明,kind
属性表示是什么类型的声明,由于 ES6 引入了 const/let
。declarations
表示声明的多个描述,由于咱们能够这样:let a = 1, b = 2;
。
interface VariableDeclaration <: Declaration { type: "VariableDeclaration"; declarations: [ VariableDeclarator ]; kind: "var"; }
变量声明的描述,id
表示变量名称节点,init
表示初始值的表达式,能够为 null
。
interface VariableDeclarator <: Node { type: "VariableDeclarator"; id: Pattern; init: Expression | null; }
表达式节点。
interface Expression <: Node { }
interface Import <: Node { type: "Import"; }
数组表达式节点,elements
属性是一个数组,表示数组的多个元素,每个元素都是一个表达式节点。
interface ArrayExpression <: Expression { type: "ArrayExpression"; elements: [ Expression | null ]; }
对象表达式节点,property
属性是一个数组,表示对象的每个键值对,每个元素都是一个属性节点。
interface ObjectExpression <: Expression { type: "ObjectExpression"; properties: [ Property ]; }
对象表达式中的属性节点。key
表示键,value
表示值,因为 ES5 语法中有 get/set
的存在,因此有一个 kind
属性,用来表示是普通的初始化,或者是 get/set
。
interface Property <: Node { type: "Property"; key: Literal | Identifier; value: Expression; kind: "init" | "get" | "set"; }
函数表达式节点。
interface FunctionExpression <: Function, Expression { type: "FunctionExpression"; }
二元运算表达式节点,left
和 right
表示运算符左右的两个表达式,operator
表示一个二元运算符。
interface BinaryExpression <: Expression { type: "BinaryExpression"; operator: BinaryOperator; left: Expression; right: Expression; }
二元运算符,全部值以下:
enum BinaryOperator { "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "<<" | ">>" | ">>>" | "+" | "-" | "*" | "/" | "%" | "|" | "^" | "&" | "in" | "instanceof" }
赋值表达式节点,operator
属性表示一个赋值运算符,left
和 right
是赋值运算符左右的表达式。
interface AssignmentExpression <: Expression { type: "AssignmentExpression"; operator: AssignmentOperator; left: Pattern | Expression; right: Expression; }
赋值运算符,全部值以下:(经常使用的并很少)
enum AssignmentOperator { "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "<<=" | ">>=" | ">>>=" | "|=" | "^=" | "&=" }
条件表达式,一般咱们称之为三元运算表达式,即 boolean ? true : false
。属性参考条件语句。
interface ConditionalExpression <: Expression { type: "ConditionalExpression"; test: Expression; alternate: Expression; consequent: Expression; }
interface Decorator <: Node { type: "Decorator"; expression: Expression; }
模式,主要在 ES6 的解构赋值中有意义,在 ES5 中,能够理解为和 Identifier
差很少的东西。
interface Pattern <: Node { }
interface Class <: Node { id: Identifier | null; superClass: Expression | null; body: ClassBody; decorators: [ Decorator ]; }
interface ClassBody <: Node { type: "ClassBody"; body: [ ClassMethod | ClassPrivateMethod | ClassProperty | ClassPrivateProperty ]; }
interface ClassMethod <: Function { type: "ClassMethod"; key: Expression; kind: "constructor" | "method" | "get" | "set"; computed: boolean; static: boolean; decorators: [ Decorator ]; }
interface ImportDeclaration <: ModuleDeclaration { type: "ImportDeclaration"; specifiers: [ ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier ]; source: Literal; }
import 声明,如: import foo from "mod";
想知道完整的核心 Babylon AST node types,可查看 babylon spec.md。这里仅仅只是列出目录,方便你们对造成清晰完整的概念。
刚开始原本是准备讲解 Babel 及经常使用模块的,后来发现内容太庞大,一篇文章根本容纳不了,因而改成只关注 Babel 的代码解析 Babylon 部分,结果随便一整理,又是这么长,唉。。。只能这样子了。