语法分析和语义分析
# 语法分析和语义分析
语法分析(syntactic analysis)的作用是在词法分析的基础上,遍历 token,从而识别出程序的抽象语法树(AST),它是一个树形结构。这个树形结构主要描述了源码的结构,暂时还缺少语义信息,比如作用域、类型检查等,我们将在语义分析的环节详细介绍。
以英文句子 "today is comfortable."
为例,对其词法分析就是将句子分离为各个单词,而语法分析关注的是单词和单词之间的关系,这些关系就是所谓的语法。
https://www.tr0y.wang/2021/04/04/%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9A%E8%AF%AD%E4%B9%89%E5%88%86%E6%9E%90/
语义分析负责分析上下文相关的情况,和具体语言相关。
# 相关概念
# 抽象语法树和具象语法树
具象语法树(concrete syntax tree, CST)
具象语法树(concrete syntax tree, CST),又称为语法树(syntax tree),它是源码的树状表示,会展示各种语法细节。
TODO(画图)
抽象语法树(abstract syntax tree, AST)
抽象语法树也是树形结构,只不过相对于具象语法树,其不会展示源码的各种语法细节。
TODO(画图)
不同点
AST 使用操作符作为根节点和内部节点,并用操作数作为子节点
TODO(画图)
与 CST 不同,AST 不适用内部节点表示语法规则
TODO(画图)
AST 不代表真实语法的每个细节,比如,它省略了括号等细节
TODO(画图)
与 CST 相比,AST 更密集
TODO(画图)
案例
TODO(画图)
# 语法分析器
语法分析器负责接收 token 列表,生成源码的抽象语法树,此时的抽象语法树,重点在描述源码的程序结构信息。
# 上下文无关文法
文法
以英文句子
"today is comfortable."
为例,文法用于定义该句子的结构,对于编程语言,文法用户定义编程语言的结构。乔姆斯基文法体系
一位叫乔姆斯基的教授准备利用数学来研究人类自然语言的规律,在此期间,他总结了很多数学工具和方法,称为"乔姆斯基文法体系"。但这套体系在计算机领域进行了发扬光大,用于对程序语言进行结构分析。
乔姆斯基文法体系有 4 类文法:
- 0 型文法: 任意文法,在程序设计语言中不常用
- 1 型文法: 上下文相关文法,在程序设计语言中不常用
- 2 型文法: 上下文无关文法(Context-free Grammar,CFG)在程序设计中用于描述程序语言的语法结构
- 3 型文法: 正则文法,就是词法分析中的正则表达式,可以用来描述程序语言的词法结构
所有文法中,0 型文法的表达能力最强,3 型文法最弱,如图:
TODO(画图)
上下文无关文法(Context-free Grammar,CFG)
上文无关文法,指的是该文法定义的所有句子结构之间是没有任何关系的,比如
ID = ID + ID
,我们不需要关注ID
是怎么来的,上下文有什么和它有关的内容,我们只需要关心一个字符是否是ID
,并且ID
的等价形式是什么。https://www.cnblogs.com/jacksplwxy/p/10052752.html
一个上下文无关文法包括四个部分: 终结符号集合,非终结符号集合,起始符号,产生式集合。
用上下文无关文法描述算术表达式
TODO
# 生成 AST 的算法
有两种方式可以生成 AST: 自顶向下分析、自底向上分析。
自顶向下分析
在自顶向下分析中,"递归下降"算法是常用的一种算法,"递归下降"算法包括两个要点: "下降"和"递归"。
下降
递归
左递归
自底向上分析
TODO
- 自顶向下分析: 根据形式语法规则,在语法分析树的自顶向下展开中搜索输入符号串可能的最左推导。单词按从左到右的顺序依次使用。
- 自底向上分析: 语法分析器从现有的输入符号串开始,尝试将其根据给定的形式语法规则进行改写,最终改写为语法的起始符号。
# 引用消解
引用消解解决的是如何找到引用的定义的问题。比如下述案例:
1> const a = 1;
2>
3> const run = () => {
4> const a = 2;
5> return a;
6> };
7>
8> console.log(run());
2
3
4
5
6
7
8
在外层环境和 run()
内部都定义了变量 a
,无论是编辑器快速跳到 a
变量的定义,还是在运行时,都能准确发现第五行的变量 a
的值是第四行的 2
。这个发现定义的过程就是引用消解。
# 左值和右值
左值和右值是开发编译器、解释器时绕不开的一个话题,什么是左值和右值呢?
a + 1
上面这个例子中,很显然取的是变量 a
的值。
a = 5
这个例子中,取的是变量 a
的地址,并将 5
赋值到该地址对应的值中,当然这部分是 JavaScript 内部的处理。
对于 Babel 这样的转译器而言,需要区分源码中,哪些属于赋值操作,哪些属于取值操作。
简单而言,左值指的就是赋值的目标,右值指的就是取值的目标。
满足左值,自然要满足一些条件:
- 节点位于赋值表达式的左边
- 节点位于函数的形参
- 一元操作符:
++
或--
- 其他需要改变变量值的情况
对于像 a + 3
、a = 5
这样的代码,对应到语法树上,都叫做 xxxx
节点,那么用什么方式区分它们是左值还是右值呢,就需要在 xxxx
节点上添加一些属性进行标记。
获取这些标记属性的过程,叫"属性计算"。
# 属性计算
属性文法
在词法分析中涉及到了正则文法,在语法分析中涉及到了上下文无关文法。而在语义分析中,涉及到了属性文法。
属性文法计算的是语义分析中,某个节点的上下文信息,比如某个节点的值(
value
)就是一种典型的属性计算。用语法分析中涉及的上下文无关文法来描述加法计算,描述如下:
add → add + mul add → mul mul → mul * primary mul → primary primary → "(" add ")" primary → integer
1
2
3
4
5
6在上述上下文无关文法的描述基础上,针对节点的
value
属性,用属性文法描述如下:dd1 → add1 + mul [ add1.value = add2.value + mul.value ] add → mul [ add.value = mul.value ] mul1 → mul2 * primary [ mul1.value = mul2.value * primary.value ] mul → primary [ mul.value = primary.value ] primary → "(" add ")" [ primary.value = add.value ] primary → integer [ primary.value = strToInt(integer.str) ]
1
2
3
4
5
6这就是属性文法定义的针对
value
的规则,规则确定后,就能通过工具计算出该属性的值。这种属性文法定义的规则,叫做语法制导的定义(Syntax directed definition,SDD),如果变成计算动作,就叫做语法制导的翻译(Syntax directed translation,SDT)。
综合属性和继承属性
综合属性(S 属性,Synthesized Attribute)
如果一种属性能够由下级节点推导出来,那么这种属性就叫 S 属性(Synthesized Attribute),也就是综合属性,能够从下级节点的属性归纳、计算为本节点的属性。
比如:
a = '1' + 2
1在语义分析阶段,Babel 等转译器已经可以知道变量
a
的类型是string
了,而这个类型是由"类型推导"而来,变量a
的类型由其两个子节点(字符串'1'
和数字2
)决定,这两个子节点的类型推导出了a
的类型。继承属性(I 属性,Inherited Attribute)
与 S 属性相对应,I 属性(Inherited Attribute)意为继承属性,它可以通过当前节点的上级节点、兄弟节点、自身节点来决定。
TODO
属性计算的时机
属性计算逻辑上位于语义分析环节,但是在实际转译逻辑执行中,进行语法分析生成 AST 的同时,属性计算等语义分析也可以同步进行。
比如,在语法分析的时候,如果某个节点的属性可以通过其他节点(父节点、兄弟节点)计算而来,那么该属性可以在构建 AST 的时候顺便执行了计算。
另外,语法分析生成 AST 后,语义分析可以根据需要,对 AST 进行多次遍历,为其进行不同的属性计算。
属性计算在语义分析中的应用
- 作用域解析
- 引用消解
- 类型推导
- 类型检查
- 类型转换
- 语义合法性检查
# 语法分析和语义分析过程
# 第1步: 确定文法规则
TODO
# 第2步: 自顶向下分析
TODO
# Babel 的语法分析和语义分析
语法分析的目的是生成源码的结构化表示,即抽象语法树(AST),此时并不需要特别关注语义方面的问题,专注生成 AST。
# 源码结构
文件 babel/packages/babel-parser/src/parser/index.js
既包含了词法分析模块,也包含了语法和语义分析模块。其定义了 Parser
类,在内部实现上有一系列的继承关系:
继承顺序 | 作用 |
---|---|
Parser | 对外提供语法分析能力 |
StatementParser | 解析声明 |
ExpressionParser | 解析表达式 |
LValParser | 解析左值 |
NodeUtils | AST节点的各种属性和方法 |
UtilParser | |
Tokenizer | 词法分析器 |
ParserErrors | 错误处理模块 |
CommentsParser | 对注释进行解析 |
BaseParser | 提供基础属性和关于插件的工具方法 |
通过观察 Babel Parser 中的继承链,我们也可以了解完整的语法分析可以分解为哪些模块。
这里先介绍一些基础模块的功能,如 BaseParser/ErrorParser
,其他模块将在介绍 Babel 的语法分析、语义分析时分别详细介绍。
# BaseParser
BaseParser
类作为继承链的基础类,提供了一些基础属性和方法,为继承链的其他类使用。
由此可以看出,"继承链"也是一种良好的代码复用的方式。
BaseParser
定义了一些属性和方法。
属性
options
babel-parser
对外暴露方法的配置项,参数类型定义在babel/packages/babel-parser/src/options.js
。sourceType
:SourceType
sourceFilename
:string
startLine
:number
allowAwaitOutsideFunction
:boolean
allowReturnOutsideFunction
:boolean
allowImportExportEverywhere
:boolean
allowSuperOutsideMethod
:boolean
plugins
:PluginList
strictMode
:boolean
ranges
:boolean
tokens
:boolean
createParenthesizedExpressions
:boolean
errorRecovery
:boolean
inModule
scope
classScope
expressionScope
prodParam
plugins
filename
exportedIdentifiers
sawUnambiguousESM
ambiguousScriptDifferentAst
state
input
length
用于获取
input
的长度,因为input
会比较长,考虑到性能优化,要获取其长度,Babel 会尽量避免直接获取其长度,而是单独维护一个 length。
方法
hasPlugin()
getPluginOption()
# CommentsParser
提供针对注释的各种处理工具。
# ParserErrors
提供了对错误的统一处理工具,并集成了词法分析、语法分析过程中的所有可能出现的错误情况。
ParserErrors
对外提供了一些工具对象ErrorCodes
export const ErrorCodes = Object.freeze({ SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", });
1
2
3
4ParsingError
type ErrorContext = { pos: number, loc: Position, missingPlugin?: Array<string>, code?: string, reasonCode?: String, }; export type ParsingError = SyntaxError & ErrorContext;
1
2
3
4
5
6
7
8ErrorTemplate
export type ErrorTemplate = { code: ErrorCode, template: string, reasonCode: string, };
1
2
3
4
5makeErrorTemplates()
export function makeErrorTemplates( messages: { [key: string]: string, }, code: ErrorCode, ): ErrorTemplates
1
2
3
4
5
6Errors / SourceTypeModuleErrors
export { ErrorMessages as Errors, SourceTypeModuleErrorMessages as SourceTypeModuleErrors, } from "./error-message"; // error-message export const SourceTypeModuleErrorMessages = makeErrorTemplates( { ImportMetaOutsideModule: `import.meta may appear only with 'sourceType: "module"'`, ImportOutsideModule: `'import' and 'export' may appear only with 'sourceType: "module"'`, }, /* code */ ErrorCodes.SourceTypeModuleError, ); export const ErrorMessages = makeErrorTemplates( { AccessorIsGenerator: "A %0ter cannot be a generator.", ArgumentsInClass: "'arguments' is only allowed in functions and class methods." // ... }, SyntaxError);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ParserErrors
集成了词法分析、语法分析过程的所有错误描述相较于
ParserError
对外暴露了哪些对象,更容易帮助我们理解转译过程的,是转译过程中会关注哪些错误。ParserError
集成了如下错误:TODO
# 位置信息
文件 babel/packages/babel-parser/src/util/location.js
定义了一系列和代码位置相关的对象和方法。
Position
类描述了目标字符在源码的行列信息(
line/column
),它是一个基础的位置类。SourceLocation
类(依赖Position
类)描述了目标字符串在源码的开始位置(
start: Position
)和结束位置(end: Position
)等信息。getLineInfo(input: string, offset: number): Position
该方法用于临时获取目标字符串的位置信息。比如出于性能考虑关闭语法分析时检测代码的位置信息后,仍可以通过该方法临时获取目标字符在源码的位置信息。
# 工具方法
节点处理类(
node
)startNode()
startNodeAt()
startNodeAtNode()
finishNode()
finishNodeAt()
resetStartLocation()
resetEndLocation()
resetStartLocationFromNode()
校验类(
util
)isRelational(op: "<" | ">"): boolean
是
<
或>
节点。isContextual(name: string): boolean
isUnparsedContextual
isLookaheadContextual
expectRelational
expectContextual
canInsertSemicolon
hasPrecedingLineBreak
hasFollowingLineBreak
isLineTerminator
semicolon
expect
assertNoSpace
unexpected
expectPlugin
expectOnePlugin
tryParse
checkExpressionErrors
isLiteralPropertyName
isPrivateName
hasPropertyAsPrivateName
isOptionalChain
isObjectProperty
isObjectMethod
操作类(
util
)addExtra(node: Node, key: string, val: any)
给
node
的extra
添加属性:key/val
。eatContextual
getPrivateNameSV
initializeScopes
enterInitialScopes
# 保留字识别
文件 babel/packages/babel-parser/src/util/identifier.js
定义了关于标识符的相关对象和方法。
该文件描述了以下单词会被认为是 JavaScript 保留字:
无条件保留字
break / case / catch / continue / debugger / default / do / else / finally / for / function / if / return / switch / throw / try / var / const / while / with / new / this / super / class / extends / export / import / null / true / false / in / instanceof / typeof / void / delete
1严格模式下的新增保留字
implements / interface / let / package / private / protected / public / static / yield
1strictBind
下的新增保留字eval / arguments
1可能是保留字
enum / await
1
工具包 @babel/helper-validator-identifier
也定义了一系列工具方法,在程序中判断保留字信息。
# 作用域处理(scope
)
什么是作用域
作用域是当前的执行上下文,也就是值和表达式在其中"可见"或可被访问到的上下文。如果一个值或表达式不在"当前的作用域中",那么它就是不可用的。
我们知道,JavaScript 中存在三种作用域: 全局作用域、函数作用域、块级作用域。并且,JavaScript 是静态作用域,也就是在静态代码解析的时候,作用域已经确认了,因此,Babel 这样的工具也可以针对作用域做一些转译工作。
Babel 针对这几种作用域做了更细致的划分,在文件
babel/packages/babel-parser/src/util/scopeflags.js
定义了一系列的作用域:export type ScopeFlags = | typeof SCOPE_OTHER | typeof SCOPE_PROGRAM | typeof SCOPE_FUNCTION | typeof SCOPE_VAR | typeof SCOPE_ARROW | typeof SCOPE_SIMPLE_CATCH | typeof SCOPE_SUPER | typeof SCOPE_DIRECT_SUPER | typeof SCOPE_CLASS | typeof SCOPE_STATIC_BLOCK
1
2
3
4
5
6
7
8
9
10
11笔者整理了 Babel 定义的作用域和 JavaScript 作用域之间的关系:
全局作用域:
SCOPE_PROGRAM
函数作用域:
SCOPE_FUNCTION
箭头函数和普通函数。
块级作用域
SCOPE_VAR
SCOPE_ARROW
箭头函数
SCOPE_SIMPLE_CATCH
try { } catch(err) { // 这个区域 }
1
2
3
4
5SCOPE_SUPER
SCOPE_DIRECT_SUPER
SCOPE_CLASS
SCOPE_STATIC_BLOCK
SCOPE_OTHER
ForStatement
SwitchStatement
TryStatement
Block
作用域栈
文件
babel/packages/babel-parser/src/util/scope.js
描述了 Babel 对作用域的各种处理。其中,该文件定义的
Scope
类维护了一个"作用域栈"(scopeStack
),并提供了入栈方法enter()
和出栈方法exit()
,如图:TODO
当 Babel 解析表达式(
expression
)、语句(statement
)等节点时,会频繁地通过递归方式进行节点解析的工作,TODO每进入一个新的节点解析时,一个新的
scope
作用域对象会入栈,在该新节点解析的过程中的上下文均以入栈的这个作用域对象为准,待新节点解析完毕,之前入栈的作用域对象就出栈。3 种作用域
this.scope
this.classScope
this.expressionScope
scope.js
文件
babel/packages/babel-parser/src/util/scope.js
描述了 Babel 对作用域的各种工具属性和方法。不同的应用场景有不同的工具对应:
inFunction
当前作用域处于某个函数体中。
allowSuper
当前作用域允许使用
super()
方法。allowDirectSuper
当前作用有允许直接使用
super()
方法。inClass
当前作用域处于
class
声明中。inClassAndNotInNonArrowFunction
当前作用域位于
class
且是非箭头函数体中。inStaticBlock
当前作用域位于
statiBlock
中。inNonArrowFunction
当前作用域位于非箭头函数体中。
treatFunctionsAsVar
当前作用域位于中,函数是否可以被认为是
var
定义的函数对象。从顶层作用域的角度看,函数声明可以被视为
var
定义的函数对象。createScope()
创建一个
scope
实例。enter()
创建一个
scope
实例,并推入到scope
栈。exit()
scope
栈推出。treatFunctionsAsVarInScope()
同
treatFunctionsAsVar()
。declareName()
TODO
maybeExportDefined()
TODO
checkRedeclarationInScope()
检查是否重复定义,如果有,会抛出错误。
isRedeclaredInScope()
判断是否重复定义,如果有,不会抛出错误。
checkLocalExport()
TODO
currentScope()
获取
scope
栈顶项。currentVarScopeFlags()
TODO
currentThisScopeFlags()
TODO
# 语句处理(statement
)
文件 babel/packages/babel-parser/src/statement.js
描述了 Babel 对语句(statement
)的各种处理。
生成语句节点
首先看一下 statement 部分,解析了哪些 AST 节点:
parseProgram --> parseBlockBody --> parseBlockOrModuleBlockBody --> while body afterBlockParse
这里列举了
statement.js
可以解析的节点列表:分类 节点名称 定义 描述 File
Program
interface Program <: Node {
type: "Program";
interpreter: InterpreterDirective | null;
sourceType: "script" | "module";
body: [ Statement | ModuleDeclaration ];
directives: [ Directive ];
}根节点,AST顶部节点 body
是一个数组,包括语句和模块声明
如果是 ES6 模块,必须指定sourceType
为module
,否则指定为script
InterpreterDirective
interface InterpreterDirective <: StringLiteral {
type: "InterpreterDirective";
}#!
字面量MemberExpression
interface MemberExpression <: Expression, Pattern {
type: "MemberExpression";
object: Expression | Super;
property: Expression | PrivateName;
computed: boolean;
}成员表达式节点
表示引用对象成员,如obj.name
或obj["name"]
1.object
: 表示对象表达式节点,如上例中的obj
2.property
: 表示属性名称,如上例中的name
3.computed
: 若为true
,表示obj["name"]
形式,property
需要为Expression
节点;若为false
,表示obj.name
形式,property
需要为Identifier
节点Decorator
interface Decorator <: Node {
type: "Decorator";
expression: Expression;
}装饰器 CallExpression
interface CallExpression <: Expression {
type: "CallExpression";
callee: Expression | Super | Import;
arguments: [ Expression | SpreadElement ];
}函数调用表达式
如run(1, 2)
这样的表达式。
1.callee
: 表示函数,是表达式节点
2.arguments
: 表示参数,为Expression
或SpreadElement
BreakStatement
interface BreakStatement <: Statement {
type: "BreakStatement";
label: Identifier | null;
}Break 语句 break [label?];
ContinueStatement
interface ContinueStatement <: Statement {
type: "ContinueStatement";
label: Identifier | null;
}Continue 语句 continue [label?];
DebuggerStatement
interface DebuggerStatement <: Statement {
type: "DebuggerStatement";
}调试语句 debugger;
DoWhileStatement
interface DoWhileStatement <: Statement {
type: "DoWhileStatement";
body: Statement;
test: Expression;
}DoWhile语句 do {[test]} while ([body])
VariableDeclaration
interface VariableDeclaration <: Declaration {
type: "VariableDeclaration";
declarations: [ VariableDeclarator ];
kind: "var" | "let" | "const";
}变量声明
如let a = 1;
1.kind
: 表示是什么类型的声明
2.declarations
: 表示声明的多个描述,比如let a = 1, b = 2;
例如:
1.const a = 1
2.let a = 1, b = 2
IfStatement
interface IfStatement <: Statement {
type: "IfStatement";
test: Expression;
consequent: Statement;
alternate: Statement | null;
}if
语句if ([test]) {[consequent]} else {[alternate]}
1.test
表示if (...)
括号中的表达式
2.consequent
表示条件为true
时的执行语句,通常是一个块语句
3.alternate
用来表示else
后跟随的语句,通常也会是块语句,也可以又是一个if
语句,例如else if (...)
,也可以是null
ReturnStatement
interface ReturnStatement <: Statement {
type: "ReturnStatement";
argument: Expression | null;
}返回语句 return [argument]
argument
为表达式或 nullSwitchCase
interface SwitchCase <: Node {
type: "SwitchCase";
test: Expression | null;
consequent: [ Statement ];
}SwitchCase节点 case: [test]: [consequent]
SwitchStatement
interface SwitchStatement <: Statement {
type: "SwitchStatement";
discriminant: Expression;
cases: [ SwitchCase ];
}switch语句 switch ([discriminant]) {[cases]}
1.discriminant
: switch 后紧跟的表达式
2.cases
: switchCase 节点数组ThrowStatement
interface ThrowStatement <: Statement {
type: "ThrowStatement";
argument: Expression;
}Throw语句 throw [argument]
CatchClause
interface CatchClause <: Node {
type: "CatchClause";
param?: Pattern;
body: BlockStatement;
}Catch从句节点
ES2019 允许 catch 从句为空:try {} catch {}
TryStatement
interface TryStatement <: Statement {
type: "TryStatement";
block: BlockStatement;
handler: CatchClause | null;
finalizer: BlockStatement | null;
}Try语句 try {[block]} catch {[handler]} finally {[finalizer]}
WhileStatement
interface WhileStatement <: Statement {
type: "WhileStatement";
test: Expression;
body: Statement;
}While语句 while ([test] {[body]}
WithStatement
interface WithStatement <: Statement {
type: "WithStatement";
object: Expression;
body: Statement;
}with 语句 with ([object]) {[body]}
EmptyStatement
interface EmptyStatement <: Statement {
type: "EmptyStatement";
}空语句 ;
LabeledStatement
interface LabeledStatement <: Statement {
type: "LabeledStatement";
label: Identifier;
body: Statement;
}标签语句 loop: … break loop;
ExpressionStatement
interface ExpressionStatement <: Statement {
type: "ExpressionStatement";
expression: Expression;
}表达式语句
如1 + 1
BlockStatement
interface BlockStatement <: Statement {
type: "BlockStatement";
body: [ Statement ];
directives: [ Directive ];
}块语句 {[body]}
例如:if (...) { // 块语句部分 }
1.body
: 内容节点列表
2.directives
: ???ForStatement
interface ForStatement <: Statement {
type: "ForStatement";
init: VariableDeclaration | Expression | null;
test: Expression | null;
update: Expression | null;
body: Statement;
}for 语句 for ([init];[test];[update]) {[body]}
1.init/test/update
: 分别表示for
语句括号中的三部分,分别为初始化值、循环判断条件、变量更新操作;三个均可以为null
,即for(;;){...}
2.body
: 循环执行的语句ForInStatement
interface ForInStatement <: Statement {
type: "ForInStatement";
left: VariableDeclaration | Expression;
right: Expression;
body: Statement;
}ForIn 语句 for ([left] in [right]) {[body]}
ForOfStatement
interface ForOfStatement <: ForInStatement {
type: "ForOfStatement";
await: boolean;
}ForOf 语句 for (let [left] of [right])
await
表示是否为异步迭代器for-await-of
:for await (const x of obj) {}
VariableDeclarator
interface VariableDeclarator <: Node {
type: "VariableDeclarator";
id: Pattern;
init: Expression | null;
}变量声明的描述
1.id
表示变量名称节点
2.init
表示初始值的表达式,可以为null
例如:a = 1
ClassDeclaration
interface ClassDeclaration <: Class, Declaration {
type: "ClassDeclaration";
id: Identifier;
}类声明 class [name] [extends] {[body]}
ClassExpression
interface ClassExpression <: Class, Expression {
type: "ClassExpression";
}类表达式 const MyClass = class [className] [extends otherClassName] { ... }
ClassBody
interface ClassBody <: Node {
type: "ClassBody";
body: [
ClassMethod |
ClassPrivateMethod |
ClassProperty |
ClassPrivateProperty |
StaticBlock
];
}ClassPrivateProperty
interface ClassPrivateProperty <: Node {
type: "ClassPrivateProperty";
key: PrivateName;
value: Expression;
static: boolean;
}ClassProperty
interface ClassProperty <: Node {
type: "ClassProperty";
key: Expression;
value: Expression;
static: boolean;
computed: boolean;
}ExportAllDeclaration
interface ExportAllDeclaration <: ModuleDeclaration {
type: "ExportAllDeclaration";
source: StringLiteral;
assertions?: [ ImportAttribute ];
}全部导出声明
如export * from './a'
ExportNamedDeclaration
interface ExportNamedDeclaration <: ModuleDeclaration {
type: "ExportNamedDeclaration";
declaration: Declaration | null;
specifiers: [ ExportSpecifier ];
source: StringLiteral | null;
assertions?: [ ImportAttribute ];
}导出具名声明
如:export { x, y as z }
、export var a = 1
、export { x } from 'a'
ExportDefaultDeclaration
interface ExportDefaultDeclaration <:
ModuleDeclaration {
type: "ExportDefaultDeclaration";
declaration:
OptFunctionDeclaration |
OptClassDeclaration |
Expression;
}默认导出声明
如export default a
ExportDefaultSpecifier
ExportNamespaceSpecifier
ExportSpecifier
interface ExportSpecifier <: ModuleSpecifier {
type: "ExportSpecifier";
exported: Identifier | StringLiteral;
local?: Identifier | StringLiteral;
}导出说明符
如export { a }
、export { a as b }
ImportDeclaration
interface ImportDeclaration <: ModuleDeclaration {
type: "ImportDeclaration";
importKind: null | "type" | "typeof" | "value";
specifiers: [
ImportSpecifier |
ImportDefaultSpecifier |
ImportNamespaceSpecifier
];
source: StringLiteral;
assertions?: [ ImportAttribute ];
}import 声明
如import a from "./a"
ImportDefaultSpecifier
interface ImportDefaultSpecifier <: ModuleSpecifier {
type: "ImportDefaultSpecifier";
}默认导入说明符
如import a from "./a"
ImportNamespaceSpecifier
interface ImportNamespaceSpecifier <: ModuleSpecifier {
type: "ImportNamespaceSpecifier";
}命名空间导入说明符
如import * as a from "./a"
ImportAttribute
interface ImportAttribute <: Node {
type: "ImportAttribute";
key: Identifier;
value: StringLiteral;
}ImportSpecifier
interface ImportSpecifier <: ModuleSpecifier {
type: "ImportSpecifier";
imported: Identifier | StringLiteral;
}导入说明符
如import { a as b } from "./a"
校验AST节点
isLet
isLetKeyword
isValidDirective
isExportDefaultSpecifier
isThisParam
canHaveLeadingDecorator
assertModuleNodeAllowed
verifyBreakContinue
shouldParseExportDeclaration
checkExport
checkDeclaration
checkDuplicateExports
# 表达式处理(expression
)
文件 babel/packages/babel-parser/src/statement.js
描述了 Babel 对语句(statement)的各种处理。
生成表达式节点
分类 节点名称 定义 描述 SequenceExpression
interface SequenceExpression <: Expression {
type: "SequenceExpression";
expressions: [ Expression ];
}Sequence 表达式
如1, 2, 3
AssignmentExpression
interface AssignmentExpression <: Expression {
type: "AssignmentExpression";
operator: AssignmentOperator;
left: Pattern | Expression;
right: Expression;
}赋值表达式节点
1.operator
: 赋值运算符
2.left/right
: 赋值运算符左右的表达式
3.left
: ???ConditionalExpression
interface ConditionalExpression <: Expression {
type: "ConditionalExpression";
test: Expression;
alternate: Expression;
consequent: Expression;
}条件表达式
就是三元运算表达式,即a > b ? c : d
LogicalExpression
interface LogicalExpression <: Expression {
type: "LogicalExpression";
operator: LogicalOperator;
left: Expression;
right: Expression;
}逻辑运算表达式
如a && b
BinaryExpression
interface BinaryExpression <: Expression {
type: "BinaryExpression";
operator: BinaryOperator;
left: Expression | PrivateName;
right: Expression;
}二元运算表达式
如a > b
1.left/right
: 运算符左右的表达式
2.operator
: 二元运算符节点
3.left
: 还可以是PrivateName
节点UnaryExpression
interface UnaryExpression <: Expression {
type: "UnaryExpression";
operator: UnaryOperator;
prefix: boolean;
argument: Expression;
}一元表达式
如typeof 1
UpdateExpression
interface UpdateExpression <: Expression {
type: "UpdateExpression";
operator: UpdateOperator;
argument: Expression;
prefix: boolean;
}Update 表达式
如++a
1.operator
:UpdateOperator
节点,即++/--
2.argument
: 参数
3.prefix
: 表示operator
是否在前OptionalMemberExpression
interface OptionalMemberExpression <: Expression {
type: "OptionalMemberExpression";
object: Expression;
property: Expression | PrivateName;
computed: boolean;
optional: boolean;
}可选链表达式 a?.b?.c
MemberExpression
interface MemberExpression <: Expression, Pattern {
type: "MemberExpression";
object: Expression | Super;
property: Expression | PrivateName;
computed: boolean;
}成员表达式节点
表示引用对象成员,如obj.name
或obj["name"]
1.object
: 表示对象表达式节点,如上例中的obj
2.property
: 表示属性名称,如上例中的name
3.computed
: 若为true
,表示obj["name"]
形式,property
需要为Expression
节点;若为false
,表示obj.name
形式,property
需要为Identifier
节点BindExpression
interface BindExpression <: Expression {
type: "BindExpression";
object: Expression | null;
callee: Expression;
}TaggedTemplateExpression
interface TaggedTemplateExpression <: Expression {
type: "TaggedTemplateExpression";
tag: Expression;
quasi: TemplateLiteral;
}带标签的模板字符串表达式
function tagFunc(...args) {
return args;
}
const setting = 'dark mode';
console.log(tagFunc`Setting ${ setting } is ${ value }!`);OptionalCallExpression
interface OptionalCallExpression <: Expression {
type: "OptionalCallExpression";
callee: Expression;
arguments: [ Expression | SpreadElement ];
optional: boolean;
}可选执行方法节点
如f?.()()
中的?.()
CallExpression
interface CallExpression <: Expression {
type: "CallExpression";
callee: Expression | Super | Import;
arguments: [ Expression | SpreadElement ];
}函数调用表达式
如run(1, 2)
这样的表达式。
1.callee
: 表示函数,是表达式节点
2.arguments
: 表示参数,为Expression
或SpreadElement
Import
interface Import <: Node {
type: "Import";
}Import 表达式 import * from './a'
ThisExpression
interface ThisExpression <: Expression {
type: "ThisExpression";
}This表达式 this
PipelinePrimaryTopicReference
DoExpression
interface DoExpression <: Expression {
type: "DoExpression";
body: BlockStatement;
}do表达式 do { ... }
Super
interface Super <: Node {
type: "Super";
}Super 表达式 super([arguments])
PrivateName
interface PrivateName <: Node {
type: "PrivateName";
id: Identifier;
}表示私有的标识符 MetaProperty
interface MetaProperty <: Expression {
type: "MetaProperty";
meta: Identifier;
property: Identifier;
}元属性 new.target
StringLiteral
interface StringLiteral <: Literal {
type: "StringLiteral";
value: string;
}字符串字面量 NumericLiteral
interface NumericLiteral <: Literal {
type: "NumericLiteral";
value: number;
}数字字面量 BigIntLiteral
interface BigIntLiteral <: Literal {
type: "BigIntLiteral";
value: string;
}BigInt 字面量 DecimalLiteral
interface DecimalLiteral <: Literal {
type: "DecimalLiteral";
value: string;
}RegExpLiteral
interface RegExpLiteral <: Literal {
type: "RegExpLiteral";
pattern: string;
flags: string;
}正则字面量
1.pattern
: 正则表达式内容字符,如/a/i
中的"a"
2.flags
:i
、g
等标志BooleanLiteral
interface BooleanLiteral <: Literal {
type: "BooleanLiteral";
value: boolean;
}布尔值字面量 NullLiteral
interface NullLiteral <: Literal {
type: "NullLiteral";
}null 字面量 SequenceExpression
interface SequenceExpression <: Expression {
type: "SequenceExpression";
expressions: [ Expression ];
}Sequence 表达式
如1, 2, 3
ParenthesizedExpression
interface ParenthesizedExpression <: Expression {
type "ParenthesizedExpression";
expression: Expression;
}括号表达式 NewExpression
interface NewExpression <: CallExpression {
type: "NewExpression";
}new
表达式
如new Date()
TemplateElement
interface TemplateElement <: Node {
type: "TemplateElement";
tail: boolean;
value: {
cooked: string | null;
raw: string;
};
}模板元素 TemplateLiteral
interface TemplateLiteral <: Expression {
type: "TemplateLiteral";
quasis: [ TemplateElement ];
expressions: [ Expression ];
}模板字面量 Hello ${name}
ObjectExpression
interface ObjectExpression <: Expression {
type: "ObjectExpression";
properties: [
ObjectProperty |
ObjectMethod |
SpreadElement
];
}Object表达式
如"{ a: 1 }"
properties
为数组,每个元素可以是:
1.ObjectProperty
节点
2.ObjectMethod
节点
3.SpreadElement
节点ObjectPattern
interface AssignmentProperty <: ObjectProperty {
value: Pattern;
}
interface ObjectPattern <: Pattern {
type: "ObjectPattern";
properties: [ AssignmentProperty | RestElement ];
}RecordExpression
interface RecordExpression <: Expression {
type: "RecordExpression";
properties: [
ObjectProperty |
ObjectMethod |
SpreadElement
];
}Record表达式,如 #{ a: 1, b: '2' }
RestElement
interface RestElement <: Pattern {
type: "RestElement";
argument: Pattern;
}fun(…args) {}
ObjectProperty
interface ObjectProperty <: ObjectMember {
type: "ObjectProperty";
shorthand: boolean;
value: Expression;
}属性节点 TupleExpression
interface TupleExpression <: Expression {
type: "TupleExpression";
elements: [ Expression | SpreadElement | null ];
}ArrayExpression
interface ArrayExpression <: Expression {
type: "ArrayExpression";
elements: [ Expression | SpreadElement | null ];
}数组表达式
如"[1, 2, 3]"
elements
为数组,表示数组的多个元素,每个元素为表达式或null
ArrowFunctionExpression
interface ArrowFunctionExpression <:
Function, Expression {
type: "ArrowFunctionExpression";
body: BlockStatement | Expression;
}箭头函数表达式 () => {[body]}
ArgumentPlaceholder
interface ArgumentPlaceholder <: Node {
type: "ArgumentPlaceholder";
}Identifier
interface Identifier <: Expression, Pattern {
type: "Identifier";
name: string;
}标识符
即自定义名称,如变量名、属性名、函数名
一个标识符可以是一个表达式(Expression
),或者是解构(Pattern
)AwaitExpression
interface AwaitExpression <: Expression {
type: "AwaitExpression";
argument: Expression | null;
}Await 表达式 await [argument]
YieldExpression
interface YieldExpression <: Expression {
type: "YieldExpression";
argument: Expression | null;
delegate: boolean;
}Yield 表达式 yield [argument]
PipelineBareFunction
PipelineTopicExpression
ModuleExpression
校验AST节点
checkProto()
checkGetterSetterParams()
checkParams()
checkReservedWord()
atPossibleAsyncArrow()
shouldParseAsyncArrow()
maybeAsyncOrAccessorProp()
isSimpleParamList()
isAwaitAllowed()
isAmbiguousAwait()
isSimpleReference()
withTopicPermittingContext()
转换AST节点
toReferencedArguments()
# 左值处理(lval
)
文件 babel/packages/babel-parser/src/lvel.js
描述了 Babel 对左值( lval
)的各种处理。
生成的左值节点
分类 节点名称 定义 描述 SpreadElement
interface SpreadElement <: Node {
type: "SpreadElement";
argument: Expression;
}Spread 表达式
如[a, …b]
RestElement
interface RestElement <: Pattern {
type: "RestElement";
argument: Pattern;
}fun(…args) {}
ArrayPattern
interface ArrayPattern <: Pattern {
type: "ArrayPattern";
elements: [ Pattern | null ];
}[ name, age ] = [ 'Jack', 13 ]
AssignmentPattern
interface AssignmentPattern <: Pattern {
type: "AssignmentPattern";
left: Pattern;
right: Expression;
}fun(a = 10) {}
校验AST节点
isAssignable()
checkLVal()
checkToRestConversion()
checkCommaAfterRest()
转换AST节点
toAssignable()
toAssignableObjectExpressionProp()
toAssignableList()
toReferencedList()
toReferencedListDeep()
# 错误处理
Babel 除了能够正确的进行语法分析和语义分析之外,错误扫描也是必不可少的工作。如果难以直观地梳理出 Babel 在语法分析和语义分析的各种细节逻辑的话,通过分析源码中的错误扫描部分,也可以帮助我们理解其分析逻辑。
笔者梳理了 babel/packages/babel-parser/
下出现过的报错,以文件为单位分别介绍各自定义的错误。
另外,所有错误的描述,定义为在 3 个对象上:
babel-parser/src/parser/error-message.js
的ErrorMessages
,使用时重命名为Errors
babel-parser/src/parser/error-message.js
的SourceTypeModuleErrorMessages
,使用时重命名为SourceTypeModuleErrors
babel-parser/src/plugins/jsx/index.js
的JsxErrors
,使用时继续用JsxErrors
以下是 Babel 在解析 AST 节点过程中会抛出的错误。
# 语句类错误(statement.js
)
export
的对象未定义Errors.ModuleExportUndefined
Export '%0' is not defined.
1案例:
babel.transformSync('export { a }', { parserOpts: { allowUndeclaredExports: false } })
1
2
3
4
5会抛出错误:
SyntaxError: unknown: Export 'a' is not defined. > 1 | export { a } | ^
1
2
3
4在严格模式下,函数必须声明在顶层作用域或者块级作用域下
Errors.StrictFunction
In strict mode code, functions can only be declared at top level or inside a block.
1if (true) function run() {}
1Babel 在解析该代码时,默认会转译为严格模式,也就会在转译时就抛出此类错误。
如果调整转译配置:
babel.transformSync(code, { parserOpts: { strictMode: false // striceMode 默认是 true } })
1
2
3
4
5那么在转译时就不会抛错了。
非严格模式下,函数必须声明在顶层作用域、块级作用域或者作为
if
语句的body
区域Errors.SloppyFunction
In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.
1这些写法都是合法的:
if(true) function run() {} { function run() {} }
1
2
3
4
5另外,在
statement.js
中,也允许函数声明出现在标签语句中,即:label: function run() {}
但这样的写法是不合法的:
while(true) function run() {}
1TODO
Errors.UnexpectedLexicalDeclaration
Lexical declaration cannot appear in a single-statement context.
1以下案例中:
if (true) let a = 1
1这个案例的转译报错并不是这个,而是
Unexpected reserved word 'let'
。import
和export
必须出现在顶层作用域。Errors.UnexpectedImportExport
'import' and 'export' may only appear at the top level.
1{ export { x } }
1
2
3在转译时就会抛出以下错误:
SyntaxError: unknown: 'import' and 'export' may only appear at the top level. (2:4) 1 | { > 2 | export { x } | ^ 3 | }
1
2
3
4
5
6async
标记的函数必须定义在顶层作用域或块级作用域Errors.AsyncFunctionInSingleStatementContext
Async functions can only be declared at the top level or inside a block.
1这种写法的源码在转译时会触发该报错:
if (true) async function f() { }
1装饰器必须作用于类的声明中
Errors.UnexpectedLeadingDecorator
Leading decorators must be attached to a class declaration.
1举例:
@track greeting = 'World';
1SyntaxError: unknown: Leading decorators must be attached to a class declaration. (1:7) > 1 | @track greeting = 'World'; | ^
1
2
3
4非法的
break
或continue
Errors.IllegalBreakContinue
Unsyntactic %0.
1表示非法的
break
或continue
。这两个词法单元只会作用于循环,如果没有写在循环里,转译时就会发现并且报错。if (true) { break; }
1会报错
Unsyntactic break
。if (true) { continue; }
1会报错
Unsyntactic continue
。Errors.ForOfLet
The left-hand side of a for-of loop may not start with 'let'.
1TODO
Errors.ForOfAsync
The left-hand side of a for-of loop may not be 'async'.
1TODO
非法的
return
Errors.IllegalReturn
'return' outside of function.
1转译时,函数体外出现了
return
会抛出此错误。比如:
return
1但如果这样配置,就允许函数体外出现
return
:babel.transformSync(code, { parserOpts: { allowReturnOutsideFunction: true } })
1
2
3
4
5switch
语句中是否有多余的default
Errors.MultipleDefaultsInSwitch
Multiple default clauses.
1switch(name) { case 'Jack': break; default: break; default: break; }
1
2
3
4
5
6
7
8throw
不允许出现换行符Errors.NewlineAfterThrow
Illegal newline after throw.
1throw 'error'
1
2Errors.NoCatchOrFinally
Missing catch or finally clause.
1try { console.log('Hi') }
1
2
3Errors.StrictWith
'with' in strict mode.
1'use strict'; with
1
2Errors.LabelRedeclaration
Label '%0' is already declared.
1TODO
Errors.ForInOfLoopInitializer
'%0' loop variable declaration may not have an initializer.
1let iterable = [10, 20, 30]; for (let value = 50 of iterable) { console.log(value); }
1
2
3
4
5转译时会抛出错误:
'for-of' loop variable declaration may not have an initializer. (3:5) 1 | let iterable = [10, 20, 30]; 2 | > 3 | for (let value = 50 of iterable) { | ^ 4 | console.log(value); 5 | }
1
2
3
4
5
6
7
8Errors.InvalidLhs
Invalid left-hand side in %0.
11 = 1
1SyntaxError: unknown: Invalid left-hand side in assignment expression. (1:0) > 1 | 1 = 1 | ^
1
2
3
4Errors.DeclarationMissingInitializer
'%0' require an initialization value.
1const x;
1SyntaxError: unknown: 'Const declarations' require an initialization value. (1:7) > 1 | const x; |
1
2
3
4Errors.GeneratorInSingleStatementContext
Generators can only be declared at the top level or inside a block.
1已知,在非严格模式下,这样的写法是合法的:
if (true) function run() {}
1但 Generator 函数例外,这样的写法是非法的:
if (true) function* run() {}
1Errors.DecoratorSemicolon
Decorators must not be followed by a semicolon.
1class XXX { @on('foo'); [Symbol()](e) { // } }
1
2
3
4
5
6// 这是伪代码 babel.transformSync(code, { plugins: [ 'babel-plugin-proposal-decorators', { decoratorsBeforeExport: true } ] })
1
2
3
4
5
6SyntaxError: unknown: Decorators must not be followed by a semicolon. (2:15) 1 | class Test { > 2 | @on('foo'); | ^ 3 | [Symbol()](e) { 4 | // 5 | }
1
2
3
4
5
6
7
8Errors.TrailingDecorator
Decorators must be attached to a class element.
1@on name ='Jack';
1SyntaxError: unknown: Leading decorators must be attached to a class declaration. (2:7) 1 | > 2 | @track greeting ='World'; |
1
2
3
4
5Errors.ConstructorIsGenerator
Constructor can't be a generator.
1class Test { * constructor() {} }
1
2
3SyntaxError: unknown: Constructor can't be a generator. (2:6) 1 | class Test { > 2 | * constructor() {} | ^ 3 | }
1
2
3
4
5
6Errors.DuplicateConstructor
Duplicate constructor in the same class.
1class Test { constructor() {} constructor() {} }
1
2
3
4SyntaxError: unknown: Duplicate constructor in the same class. (3:4) 1 | class Test { 2 | constructor() {} > 3 | constructor() {} | ^ 4 | }
1
2
3
4
5
6
7Errors.OverrideOnConstructor
'override' modifier cannot appear on a constructor declaration.
1TODO
Errors.ConstructorIsAsync
Constructor can't be an async function.
1class Test { async constructor() {} }
1
2
3SyntaxError: unknown: Constructor can't be an async function. (2:10) 1 | class Test { > 2 | async constructor() {} | ^ 3 | }
1
2
3
4
5
6Errors.ConstructorIsAccessor
Class constructor may not be an accessor.
1TODO
Errors.StaticPrototype
Classes may not have static property named prototype.
1class X { static prototype; }
1
2
3SyntaxError: unknown: Classes may not have static property named prototype. (2:11) 1 | class X { > 2 | static prototype; | ^ 3 | }
1
2
3
4
5
6Errors.ConstructorClassPrivateField
Classes may not have a private field named '#constructor'.
1class Test { #constructor() {} }
1
2
3SyntaxError: unknown: Classes may not have a private field named '#constructor'. (2:4) 1 | class Test { > 2 | #constructor() {} | ^ 3 | }
1
2
3
4
5
6Errors.DecoratorStaticBlock
Decorators can't be used with a static block.
1class X { constructor() { } @decorator() static { } }
1
2
3
4
5
6
7
8
9
10SyntaxError: unknown: Decorators can't be used with a static block. (6:4) 4 | } 5 | > 6 | @decorator() | ^ 7 | static { 8 | 9 | }
1
2
3
4
5
6
7
8
9Errors.ConstructorClassField
Classes may not have a field named 'constructor'.
1class X { constructor = function () { } }
1
2
3
4
5SyntaxError: unknown: Classes may not have a field named 'constructor'. (2:4) 1 | class X { > 2 | constructor = function () { | ^ 3 | 4 | } 5 | }
1
2
3
4
5
6
7
8Errors.DecoratorBeforeExport
Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax.
1export @decorator class {}
1> 1 | export @decorator class {} | ^ 2 |
1
2
3Errors.UnsupportedDefaultExport
Only expressions, functions or classes are allowed as the `default` export.
1export default const x = 1
1SyntaxError: unknown: Only expressions, functions or classes are allowed as the `default` export. (1:15) > 1 | export default const x = 1 | ^ 2 |
1
2
3
4
5Errors.ExportDefaultFromAsIdentifier
'from' is not allowed as an identifier after 'export default'.
1const from = 1 export default from;
1
2SyntaxError: unknown: 'from' is not allowed as an identifier after 'export default'. (2:15) 1 | const from = 1 > 2 | export default from; | ^
1
2
3
4
5Errors.ExportBindingIsString
A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '%0' as '%1' } from 'some-module'`?
1TODO
Errors.UnsupportedDecoratorExport
A decorated export must export a class declaration.
1TODO
Errors.DuplicateDefaultExport
Only one default export allowed per module.
1export default x export default y
1
2SyntaxError: unknown: Only one default export allowed per module. (2:0) 1 | export default x > 2 | export default y |
1
2
3
4
5Errors.DuplicateExport
`%0` has already been exported. Exported identifiers must be unique.
1export { x } export { x }
1
21 | export { x } > 2 | export { x } | ^
1
2
3Errors.ModuleExportNameHasLoneSurrogate
An export name cannot include a lone surrogate, found '\\u%0'.
1TODO
Errors.ModuleAttributesWithDuplicateKeys
Duplicate key "%0" is not allowed in module attributes.
1TODO
Errors.ModuleAttributeDifferentFromType
The only accepted module attribute is `type`.
1TODO
Errors.DestructureNamedImport
ES2015 named imports do not destructure. Use another statement for destructuring after the import.
1TODO
Errors.ImportBindingIsString
A string literal cannot be used as an imported binding.\n- Did you mean `import { "%0" as foo }`?
1TODO
SourceTypeModuleErrors.ImportOutsideModule
'import' and 'export' may appear only with 'sourceType: "module"'
1export { x }
1SyntaxError: unknown: 'import' and 'export' may appear only with 'sourceType: "module"' (1:0) Consider renaming the file to '.mjs', or setting sourceType:module or sourceType:unambiguous in your Babel config for this file. > 1 | export { x } | ^
1
2
3
4
5
# 表达式类错误(expression.js
)
SourceTypeModuleErrors.ImportMetaOutsideModule
import.meta may appear only with 'sourceType: "module"'
1const code = 'import.meta' babel.transformSync('import.meta', { parserOpts: { sourceType: 'script' } })
1
2
3
4
5
6
7SyntaxError: unknown: import.meta may appear only with 'sourceType: "module"' (1:0) Consider renaming the file to '.mjs', or setting sourceType:module or sourceType:unambiguous in your Babel config for this file. > 1 | import.meta | ^
1
2
3
4
5Errors.RecordNoProto
'__proto__' is not allowed in Record expressions.
1TODO
Errors.DuplicateProto
Redefinition of __proto__ property.
1TODO
Errors.UnexpectedAwaitAfterPipelineBody
Unexpected "await" after pipeline body; await must have parentheses in minimal proposal.
1TODO
Errors.MixingCoalesceWithLogical
Nullish coalescing operator(??) requires parens when mixing with logical operators.
1TODO
Errors.UnexpectedTokenUnaryExponentiation
Illegal expression. Wrap left hand side or entire exponentiation in parentheses.
1Errors.StrictDelete
Deleting local variable in strict mode.
1Errors.DeletePrivateField
Deleting a private field is not allowed.
1Errors.SuperPrivateField
Private fields can't be accessed on super.
1Errors.OptionalChainingNoTemplate
https://github.com/tc39/proposal-optional-chaining/issues/54
Tagged Template Literals are not allowed in optionalChain.
1Errors.ImportCallArity
'import()' requires exactly %0.
1import()
1SyntaxError: unknown: `import()` requires exactly one argument. (1:0) > 1 | import() | ^
1
2
3
4Errors.ImportCallSpreadArgument
'...' is not allowed in 'import()'.
1import(...x)
1SyntaxError: unknown: `...` is not allowed in `import()`. (1:7) > 1 | import(...x) | ^ 2 |
1
2
3
4
5Errors.ImportCallArgumentTrailingComma
Trailing comma is disallowed inside import(...) arguments.
1import('./x.js', )
1SyntaxError: unknown: Trailing comma is disallowed inside import(...) arguments. (1:15) > 1 | import('./x.js', ) | ^
1
2
3
4Errors.UnsupportedImport
'import' can only be used in 'import()' or 'import.meta'.
1Errors.UnsupportedBind
Binding should be performed on object property.
1Errors.PrivateInExpectedIn
Private names are only allowed in property accesses (`obj.#%0`) or in `in` expressions (`#%0 in obj`).
1Errors.PrimaryTopicRequiresSmartPipeline
Primary Topic Reference found but pipelineOperator not passed 'smart' for 'proposal' option.
1Errors.PrimaryTopicNotAllowed
Topic reference was used in a lexical context without topic binding.
1Errors.LineTerminatorBeforeArrow
No line break is allowed before '=>'.
1Errors.UnsupportedSuper
'super' can only be used with function calls (i.e. super()) or in property accesses (i.e. super.prop or super[prop]).
1示例1:
super()
1SyntaxError: unknown: `super()` is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class? (1:0) > 1 | super() | ^ 2 |
1
2
3
4
5示例2:
class Run { constructor() { { super() } } }
1
2
3
4
5
6
7SyntaxError: unknown: `super()` is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class? (4:12) 2 | constructor() { 3 | { > 4 | super() | ^ 5 | } 6 | } 7 | }
1
2
3
4
5
6
7
8
9Errors.UnexpectedPrivateField
Private names can only be used as the name of a class element (i.e. "class C { #p = 42; #m() {} }") or a property of member expression (i.e. "this.#p").
1
2
3
4const obj = { #name: 'Jack' }
1
2
31 | const obj = { > 2 | #name: 'Jack' | ^ 3 | }
1
2
3
4Errors.UnsupportedMetaProperty
The only valid meta property for %0 is %0.%1.
1import.name
1SyntaxError: unknown: The only valid meta property for import is import.meta. (1:7) > 1 | import.name | ^
1
2
3
4Errors.UnexpectedNewTarget
"new.target" can only be used in functions or class properties.
1new.target
1SyntaxError: unknown: `new.target` can only be used in functions or class properties. (1:0) > 1 | new.target | ^
1
2
3
4Errors.ImportCallNotNewExpression
Cannot use new with import(...).
1new import(x)
1SyntaxError: unknown: Cannot use new with import(...). (1:4) > 1 | new import(x) | ^
1
2
3
4Errors.OptionalChainingNoNew
Constructors in/after an Optional Chain are not allowed.
1const person = { name: class { } } const man = new person?.name()
1
2
3
4
5
6
7SyntaxError: unknown: Constructors in/after an Optional Chain are not allowed. (7:28) 5 | } 6 | > 7 | const man = new person?.name() |
1
2
3
4
5
6Errors.InvalidEscapeSequenceTemplate
Invalid escape sequence in template.
1Errors.InvalidRecordProperty
Only properties and spread elements are allowed in record definitions.
1Errors.UnsupportedPropertyDecorator
Decorators cannot be used to decorate object literal properties.
1Errors.AccessorIsGenerator
A %0ter cannot be a generator.
1Errors.BadSetterRestParameter
A 'set' accesor function argument must not be a rest parameter.
1Errors.IllegalLanguageModeDirective
Illegal 'use strict' directive in function with non-simple parameter list.
1Errors.UnexpectedToken
Unexpected token '%0'.
1Errors.UnexpectedArgumentPlaceholder
Unexpected argument placeholder.
1Errors.YieldBindingIdentifier
Can not use 'yield' as identifier inside a generator.
1const run = function* () { const yield = 1; }
1
2
31 | const run = function* () { > 2 | const yield = 1; | ^ 3 | }
1
2
3
4Errors.AwaitBindingIdentifier
Can not use 'await' as identifier inside an async function.
1const run = async () => { const await = 1; }
1
2
32 | const run = async () => { > 3 | const await = 1; | ^ 4 | }
1
2
3
4Errors.AwaitBindingIdentifierInStaticBlock
Can not use 'await' as identifier inside a static block.
1Errors.ArgumentsInClass
'arguments' is only allowed in functions and class methods.
1Errors.UnexpectedKeyword
Unexpected keyword '%0'.
1Errors.UnexpectedReservedWord
Unexpected reserved word '%0'.
1Errors.ObsoleteAwaitStar
'await*' has been removed from the async functions proposal. Use Promise.all() instead.
1Errors.PipelineHeadSequenceExpression
Pipeline head should not be a comma-separated sequence expression.
1Errors.PipelineBodySequenceExpression
Pipeline body may not be a comma-separated sequence expression.
1Errors.PipelineTopicUnused
Pipeline is in topic style but does not use topic reference.
1
# 左值类错误(lval.js
)
Errors.InvalidParenthesizedAssignment
Invalid parenthesized assignment pattern.
1const add = (a, b) = { return a + b; };
1SyntaxError: unknown: Invalid parenthesized assignment pattern. (1:13) > 1 | const add = (a, b) = { return a + b; }; | ^
1
2
3
4Errors.MissingEqInAssignment
Only '=' operator can be used for specifying default value.
1Errors.UnsupportedParameterDecorator
Decorators cannot be used to decorate parameters.
1class MyClass { myFunction(@MyDecorator myArg){ } }
1
2
3
4
5SyntaxError: unknown: Decorators cannot be used to decorate parameters. (2:15) 1 | class MyClass { > 2 | myFunction(@MyDecorator myArg){ | ^ 3 | 4 | } 5 | }
1
2
3
4
5
6
7
8Errors.ParamDupe
Argument name clash.
1function run(req, req) { }
1
2
3SyntaxError: unknown: Argument name clash. (1:18) > 1 | function run(req, req) { | ^ 2 | 3 | } 4 |
1
2
3
4
5
6
7Errors.LetInLexicalBinding
'let' is not allowed to be used as a name in 'let' or 'const' declarations.
1let let = 1
1SyntaxError: unknown: 'let' is not allowed to be used as a name in 'let' or 'const' declarations. (1:4) > 1 | let let = 1 | ^
1
2
3
4Errors.InvalidPropertyBindingPattern
Binding member expression.
1Errors.InvalidLhs
Invalid left-hand side in %0.
1if( x == 1 || y = 2) { }
1
2
3SyntaxError: unknown: Invalid left-hand side in assignment expression. (1:4) > 1 | if( x == 1 || y = 2) { | ^ 2 | 3 | } 4 |
1
2
3
4
5
6
7Errors.InvalidLhsBinding
Binding invalid left-hand side in %0.
1arr.sort((a - b) => { return a - b })
1
2
3SyntaxError: unknown: Binding invalid left-hand side in function parameter list. (1:12) > 1 | data1.sort((a - b) => { | ^ 2 | return a.count - b.count 3 | }) 4 |
1
2
3
4
5
6
7Errors.InvalidRestAssignmentPattern
Invalid rest operator's argument.
1Errors.ElementAfterRest
Rest element must be last element.
1const run = ({ ...param1, param2 }) => {}
1SyntaxError: unknown: Rest element must be last element. (1:15) > 1 | const run = ({ ...param1, param2 }) => {} | ^
1
2
3
4Errors.RestTrailingComma
Unexpected trailing comma after rest element.
1const [ a, b, ...c, ] = [1, 2, 3];
1SyntaxError: unknown: Unexpected trailing comma after rest element. (1:18) > 1 | const [ a, b, ...c, ] = [1, 2, 3]; | ^
1
2
3
4
# 作用域类错误
class-scope.js
Errors.InvalidPrivateFieldResolution
Private name #%0 is not defined.
1Errors.PrivateNameRedeclaration
Duplicate private name #%0.
1
scope.js
Errors.VarRedeclaration
Identifier '%0' has already been declared.
1const x = 1; const x = 2;
1
2SyntaxError: unknown: Identifier 'x' has already been declared. (2:6) 1 | const x = 1; > 2 | const x = 2 | ^
1
2
3
4
5
# jsx 类错误
ErrorCodes.SyntaxError
BABEL_PARSER_SYNTAX_ERROR
1Errors.UnterminatedString
Unterminated string constant.
1function getName() { return 'Jack" }
1
2
3SyntaxError: unknown: Unterminated string constant. (2:11) 1 | function getName() { > 2 | return 'Jack" | ^ 3 | } 4 |
1
2
3
4
5
6
7JsxErrors.UnterminatedJsxContent
Unterminated JSX contents.
1function getComponent() { return ( <div> ) }
1
2
3
4
5SyntaxError: unknown: Unterminated JSX contents. (3:13) 1 | function getComponent() { 2 | return ( > 3 | <div> | ^ 4 | ) 5 | } 6 |
1
2
3
4
5
6
7
8
9JsxErrors.AttributeIsEmpty
JSX attributes must only be assigned a non-empty expression.
1function getComponent() { return ( <div title={}></div> ) }
1
2
3
4
5SyntaxError: unknown: JSX attributes must only be assigned a non-empty expression. (3:19) 1 | function getComponent() { 2 | return ( > 3 | <div title={}></div> | ^ 4 | ) 5 | } 6 |
1
2
3
4
5
6
7
8
9JsxErrors.UnsupportedJsxValue
JSX value should be either an expression or a quoted JSX text.
1function getComponent() { return ( <div title=true></div> ) }
1
2
3
4
5SyntaxError: unknown: JSX value should be either an expression or a quoted JSX text. (3:19) 1 | function getComponent() { 2 | return ( > 3 | <div title=true></div> | ^ 4 | ) 5 | } 6 |
1
2
3
4
5
6
7
8
9JsxErrors.UnexpectedSequenceExpression
Sequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?
1JsxErrors.MissingClosingTagFragment
Expected corresponding JSX closing tag for <>.
1JsxErrors.UnwrappedAdjacentJSXElements
Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?
1function getComponent() { return ( <Comp1 /> <Comp2 /> ) }
1
2
3
4
5
6SyntaxError: unknown: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (4:8) 2 | return ( 3 | <Comp1 /> > 4 | <Comp2 /> | ^ 5 | ) 6 | } 7 |
1
2
3
4
5
6
7
8
9
# 特定语法开关
Babel 并不会开启对所有语法特性的转译,这也是出于性能的考虑。
在源码中,和"语法开关"相关的代码主要是文件 babel/packages/babel-parser/src/parser/util.js
定义的方法:
expectPlugin(pluginName)
: 检测某个插件是否被配置,如果没有会抛出错误getPluginOption()
: 获取插件的配置项
以下特性默认不会开启,关于每个特性的详细介绍,可查看章节"Babel 内置插件"了解,下表中插件名称均以源码文件命名为准。
pipelineOperator
语法插件:
babel-plugin-syntax-pipeline-operator
转换插件:
babel-plugin-proposal-pipeline-operator
开启对
pipelineOperator
语法的支持,也就是|>
写法。throwExpressions
语法插件:
babel-plugin-syntax-throw-expressions
转换插件:
babel-plugin-proposal-throw-expressions
开启对
throwExpressions
的语法支持。举例:
function save(filename = throw new TypeError("Argument required")) {}
1importAssertions
语法插件:
babel-plugin-syntax-import-assertions
开启对
Import assertions
语法的支持。该语法主要是为了更清晰地描述模块的一些特性信息,比如类型。
举个例子,引入
./config.json
文件时,声明其内容类型为 json,有几种写法:静态 import
import config from './config.json' assert { type: 'json' }
1动态 import
import('./config.json', { assert: { type: 'json' } })
1export
export { default as config } from './config.json' assert { type: 'json' }
1
privateIn
语法插件:
babel-plugin-syntax-private-property-in-object
转换插件:
babel-plugin-proposal-private-property-in-object
class Foo { #bar = "bar"; test(obj) { return #bar in obj; } }
1
2
3
4
5
6
7doExpressions
语法插件:
babel-plugin-syntax-do-expressions
转换插件:
babel-plugin-proposal-do-expression
开启对
do
表达式语法的支持。do
表示可以认为是三元表达式的变种,在较复杂的情况下加,用do
表达式更简洁。let x = 100; let y = 20; let a = do { if(x > 10) { if(y > 20) { 'big x, big y'; } else { 'big x, small y'; } } else { if(y > 10) { 'small x, big y'; } else { 'small x, small y'; } } };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18functionSent
语法插件:
babel-plugin-syntax-function-sent
转换插件:
babel-plugin-proposal-function-sent
开启对
functionSent
语法的支持,即开启对function.sent
的语法支持。function* generator() { console.log("Sent", function.sent); console.log("Yield", yield); } const iterator = generator(); iterator.next(1); // Logs "Sent 1" iterator.next(2); // Logs "Yield 2"
1
2
3
4
5
6
7
8recordAndTuple
语法插件:
babel-plugin-syntax-record-and-tuple
开启对
recordAndTuple
语法的支持,对应的是proposal-record-tuple
提案。partialApplication
语法插件:
babel-plugin-syntax-partial-application
转换插件:
babel-plugin-proposal-partial-application
f(x, ?) // partial application from left f(?, x) // partial application from right f(?, x, ?) // partial application for any arg
1
2
3moduleBlocks
语法插件:
babel-plugin-syntax-module-blocks
开启对
moduleBlocks
语法的支持。moduleBlocks
语法简介如下(案例来自tc39):let moduleBlock = module { export let y = 1; }; let moduleExports = await import(moduleBlock); assert(moduleExports.y === 1); assert(await import(moduleBlock) === moduleExports); // cached in the module map
1
2
3
4
5
6
7这样,提供了非文件的方式引入模块。
且
moduleBlocks
只能被import()
引入,而非import
表达式。classStaticBlock
语法插件:
babel-plugin-syntax-class-static-block
转换插件:
babel-plugin-proposal-class-static-block
转换class类中的静态block。
下例来自Babel官网:
下例的转换需要在该插件前引入插件 babel-plugin-proposal-class-properties,也就是支持对
class
属性的语法支持和代码转换。class C { static #x = 42; static y; static { try { this.y = doSomethingWith(this.#x); } catch { this.y = "unknown"; } } }
1
2
3
4
5
6
7
8
9
10
11classPrivateProperties
语法插件:
babel-plugin-syntax-class-properties
转换插件:
babel-plugin-proposal-class-properties
开启对类的属性(
classProperties
)、私有属性(classPrivateProperties
)、私有方法(classPrivateMethods
)的语法支持。举例:
class Man { // 属性 name: 'Jack', // 私有属性 private age: 18, // 私有方法 private say() { console.log('Hi!'); } }
1
2
3
4
5
6
7
8
9
10
11
12classPrivateMethods
语法插件:
plugin-syntax-class-properties
转换插件:
babel-plugin-proposal-private-methods
class Counter extends HTMLElement { #xValue = 0; get #x() { return this.#xValue; } set #x(value) { this.#xValue = value; } }
1
2
3
4
5
6
7
8
9
10classProperties
语法插件:
babel-plugin-syntax-class-properties
转换插件:
babel-plugin-proposal-class-properties
转换 class 中的原型和静态属性/方法。
下例来自 Babel 官网。
class Bork { // Property initializer syntax instanceProperty = "bork"; boundFunction = () => { return this.instanceProperty; }; // Static class properties static staticProperty = "babelIsCool"; static staticFunction = function() { return Bork.staticProperty; }; }
1
2
3
4
5
6
7
8
9
10
11
12
13exportDefaultFrom
语法插件:
babel-plugin-syntax-export-default-from
转换插件:
babel-plugin-proposal-export-default-from
export v from "mod";
1moduleStringNames
语法插件:
babel-plugin-syntax-module-string-names
开启对
moduleStringNames
语法的支持,也就是模块别名的支持。export { hi as 'hello' } from './hi.js';
1moduleAttributes
decimal
语法插件:
babel-plugin-syntax-demical
# 总结
TODO