语法分析和语义分析
# 语法分析和语义分析
语法分析(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 → integer1
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' + 21在语义分析阶段,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 定义了一些属性和方法。
属性
optionsbabel-parser对外暴露方法的配置项,参数类型定义在babel/packages/babel-parser/src/options.js。sourceType:SourceTypesourceFilename:stringstartLine:numberallowAwaitOutsideFunction:booleanallowReturnOutsideFunction:booleanallowImportExportEverywhere:booleanallowSuperOutsideMethod:booleanplugins:PluginListstrictMode:booleanranges:booleantokens:booleancreateParenthesizedExpressions:booleanerrorRecovery:boolean
inModulescopeclassScopeexpressionScopeprodParampluginsfilenameexportedIdentifierssawUnambiguousESMambiguousScriptDifferentAststateinputlength用于获取
input的长度,因为input会比较长,考虑到性能优化,要获取其长度,Babel 会尽量避免直接获取其长度,而是单独维护一个 length。
方法
hasPlugin()getPluginOption()
# CommentsParser
提供针对注释的各种处理工具。
# ParserErrors
提供了对错误的统一处理工具,并集成了词法分析、语法分析过程中的所有可能出现的错误情况。
ParserErrors对外提供了一些工具对象ErrorCodesexport const ErrorCodes = Object.freeze({ SyntaxError: "BABEL_PARSER_SYNTAX_ERROR", SourceTypeModuleError: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", });1
2
3
4ParsingErrortype ErrorContext = { pos: number, loc: Position, missingPlugin?: Array<string>, code?: string, reasonCode?: String, }; export type ParsingError = SyntaxError & ErrorContext;1
2
3
4
5
6
7
8ErrorTemplateexport type ErrorTemplate = { code: ErrorCode, template: string, reasonCode: string, };1
2
3
4
5makeErrorTemplates()export function makeErrorTemplates( messages: { [key: string]: string, }, code: ErrorCode, ): ErrorTemplates1
2
3
4
5
6Errors / SourceTypeModuleErrorsexport { 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): booleanisUnparsedContextualisLookaheadContextualexpectRelationalexpectContextualcanInsertSemicolonhasPrecedingLineBreakhasFollowingLineBreakisLineTerminatorsemicolonexpectassertNoSpaceunexpectedexpectPluginexpectOnePlugintryParsecheckExpressionErrorsisLiteralPropertyNameisPrivateNamehasPropertyAsPrivateNameisOptionalChainisObjectPropertyisObjectMethod
操作类(
util)addExtra(node: Node, key: string, val: any)给
node的extra添加属性:key/val。eatContextualgetPrivateNameSVinitializeScopesenterInitialScopes
# 保留字识别
文件 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 / delete1严格模式下的新增保留字
implements / interface / let / package / private / protected / public / static / yield1strictBind下的新增保留字eval / arguments1可能是保留字
enum / await1
工具包 @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_BLOCK1
2
3
4
5
6
7
8
9
10
11笔者整理了 Babel 定义的作用域和 JavaScript 作用域之间的关系:
全局作用域:
SCOPE_PROGRAM函数作用域:
SCOPE_FUNCTION箭头函数和普通函数。
块级作用域
SCOPE_VARSCOPE_ARROW箭头函数
SCOPE_SIMPLE_CATCHtry { } catch(err) { // 这个区域 }1
2
3
4
5SCOPE_SUPERSCOPE_DIRECT_SUPERSCOPE_CLASSSCOPE_STATIC_BLOCKSCOPE_OTHERForStatementSwitchStatementTryStatementBlock
作用域栈
文件
babel/packages/babel-parser/src/util/scope.js描述了 Babel 对作用域的各种处理。其中,该文件定义的
Scope类维护了一个"作用域栈"(scopeStack),并提供了入栈方法enter()和出栈方法exit(),如图:TODO
当 Babel 解析表达式(
expression)、语句(statement)等节点时,会频繁地通过递归方式进行节点解析的工作,TODO每进入一个新的节点解析时,一个新的
scope作用域对象会入栈,在该新节点解析的过程中的上下文均以入栈的这个作用域对象为准,待新节点解析完毕,之前入栈的作用域对象就出栈。3 种作用域
this.scopethis.classScopethis.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可以解析的节点列表:分类 节点名称 定义 描述 FilePrograminterface Program <: Node {
type: "Program";
interpreter: InterpreterDirective | null;
sourceType: "script" | "module";
body: [ Statement | ModuleDeclaration ];
directives: [ Directive ];
}根节点,AST顶部节点 body是一个数组,包括语句和模块声明
如果是 ES6 模块,必须指定sourceType为module,否则指定为scriptInterpreterDirectiveinterface InterpreterDirective <: StringLiteral {
type: "InterpreterDirective";
}#!字面量MemberExpressioninterface 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节点Decoratorinterface Decorator <: Node {
type: "Decorator";
expression: Expression;
}装饰器 CallExpressioninterface CallExpression <: Expression {
type: "CallExpression";
callee: Expression | Super | Import;
arguments: [ Expression | SpreadElement ];
}函数调用表达式
如run(1, 2)这样的表达式。
1.callee: 表示函数,是表达式节点
2.arguments: 表示参数,为Expression或SpreadElementBreakStatementinterface BreakStatement <: Statement {
type: "BreakStatement";
label: Identifier | null;
}Break 语句 break [label?];ContinueStatementinterface ContinueStatement <: Statement {
type: "ContinueStatement";
label: Identifier | null;
}Continue 语句 continue [label?];DebuggerStatementinterface DebuggerStatement <: Statement {
type: "DebuggerStatement";
}调试语句 debugger;DoWhileStatementinterface DoWhileStatement <: Statement {
type: "DoWhileStatement";
body: Statement;
test: Expression;
}DoWhile语句 do {[test]} while ([body])VariableDeclarationinterface 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 = 2IfStatementinterface 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 (...),也可以是nullReturnStatementinterface ReturnStatement <: Statement {
type: "ReturnStatement";
argument: Expression | null;
}返回语句 return [argument]argument为表达式或 nullSwitchCaseinterface SwitchCase <: Node {
type: "SwitchCase";
test: Expression | null;
consequent: [ Statement ];
}SwitchCase节点 case: [test]: [consequent]SwitchStatementinterface SwitchStatement <: Statement {
type: "SwitchStatement";
discriminant: Expression;
cases: [ SwitchCase ];
}switch语句 switch ([discriminant]) {[cases]}
1.discriminant: switch 后紧跟的表达式
2.cases: switchCase 节点数组ThrowStatementinterface ThrowStatement <: Statement {
type: "ThrowStatement";
argument: Expression;
}Throw语句 throw [argument]CatchClauseinterface CatchClause <: Node {
type: "CatchClause";
param?: Pattern;
body: BlockStatement;
}Catch从句节点
ES2019 允许 catch 从句为空:try {} catch {}TryStatementinterface TryStatement <: Statement {
type: "TryStatement";
block: BlockStatement;
handler: CatchClause | null;
finalizer: BlockStatement | null;
}Try语句 try {[block]} catch {[handler]} finally {[finalizer]}WhileStatementinterface WhileStatement <: Statement {
type: "WhileStatement";
test: Expression;
body: Statement;
}While语句 while ([test] {[body]}WithStatementinterface WithStatement <: Statement {
type: "WithStatement";
object: Expression;
body: Statement;
}with 语句 with ([object]) {[body]}EmptyStatementinterface EmptyStatement <: Statement {
type: "EmptyStatement";
}空语句 ;LabeledStatementinterface LabeledStatement <: Statement {
type: "LabeledStatement";
label: Identifier;
body: Statement;
}标签语句 loop: … break loop;ExpressionStatementinterface ExpressionStatement <: Statement {
type: "ExpressionStatement";
expression: Expression;
}表达式语句
如1 + 1BlockStatementinterface BlockStatement <: Statement {
type: "BlockStatement";
body: [ Statement ];
directives: [ Directive ];
}块语句 {[body]}
例如:if (...) { // 块语句部分 }
1.body: 内容节点列表
2.directives: ???ForStatementinterface 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: 循环执行的语句ForInStatementinterface ForInStatement <: Statement {
type: "ForInStatement";
left: VariableDeclaration | Expression;
right: Expression;
body: Statement;
}ForIn 语句 for ([left] in [right]) {[body]}ForOfStatementinterface ForOfStatement <: ForInStatement {
type: "ForOfStatement";
await: boolean;
}ForOf 语句 for (let [left] of [right])await表示是否为异步迭代器for-await-of:for await (const x of obj) {}VariableDeclaratorinterface VariableDeclarator <: Node {
type: "VariableDeclarator";
id: Pattern;
init: Expression | null;
}变量声明的描述
1.id表示变量名称节点
2.init表示初始值的表达式,可以为null
例如:a = 1ClassDeclarationinterface ClassDeclaration <: Class, Declaration {
type: "ClassDeclaration";
id: Identifier;
}类声明 class [name] [extends] {[body]}ClassExpressioninterface ClassExpression <: Class, Expression {
type: "ClassExpression";
}类表达式 const MyClass = class [className] [extends otherClassName] { ... }ClassBodyinterface ClassBody <: Node {
type: "ClassBody";
body: [
ClassMethod |
ClassPrivateMethod |
ClassProperty |
ClassPrivateProperty |
StaticBlock
];
}ClassPrivatePropertyinterface ClassPrivateProperty <: Node {
type: "ClassPrivateProperty";
key: PrivateName;
value: Expression;
static: boolean;
}ClassPropertyinterface ClassProperty <: Node {
type: "ClassProperty";
key: Expression;
value: Expression;
static: boolean;
computed: boolean;
}ExportAllDeclarationinterface ExportAllDeclaration <: ModuleDeclaration {
type: "ExportAllDeclaration";
source: StringLiteral;
assertions?: [ ImportAttribute ];
}全部导出声明
如export * from './a'ExportNamedDeclarationinterface 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'ExportDefaultDeclarationinterface ExportDefaultDeclaration <:
ModuleDeclaration {
type: "ExportDefaultDeclaration";
declaration:
OptFunctionDeclaration |
OptClassDeclaration |
Expression;
}默认导出声明
如export default aExportDefaultSpecifierExportNamespaceSpecifierExportSpecifierinterface ExportSpecifier <: ModuleSpecifier {
type: "ExportSpecifier";
exported: Identifier | StringLiteral;
local?: Identifier | StringLiteral;
}导出说明符
如export { a }、export { a as b }ImportDeclarationinterface ImportDeclaration <: ModuleDeclaration {
type: "ImportDeclaration";
importKind: null | "type" | "typeof" | "value";
specifiers: [
ImportSpecifier |
ImportDefaultSpecifier |
ImportNamespaceSpecifier
];
source: StringLiteral;
assertions?: [ ImportAttribute ];
}import 声明
如import a from "./a"ImportDefaultSpecifierinterface ImportDefaultSpecifier <: ModuleSpecifier {
type: "ImportDefaultSpecifier";
}默认导入说明符
如import a from "./a"ImportNamespaceSpecifierinterface ImportNamespaceSpecifier <: ModuleSpecifier {
type: "ImportNamespaceSpecifier";
}命名空间导入说明符
如import * as a from "./a"ImportAttributeinterface ImportAttribute <: Node {
type: "ImportAttribute";
key: Identifier;
value: StringLiteral;
}ImportSpecifierinterface ImportSpecifier <: ModuleSpecifier {
type: "ImportSpecifier";
imported: Identifier | StringLiteral;
}导入说明符
如import { a as b } from "./a"校验AST节点
isLetisLetKeywordisValidDirectiveisExportDefaultSpecifierisThisParamcanHaveLeadingDecoratorassertModuleNodeAllowedverifyBreakContinueshouldParseExportDeclarationcheckExportcheckDeclarationcheckDuplicateExports
# 表达式处理(expression)
文件 babel/packages/babel-parser/src/statement.js 描述了 Babel 对语句(statement)的各种处理。
生成表达式节点
分类 节点名称 定义 描述 SequenceExpressioninterface SequenceExpression <: Expression {
type: "SequenceExpression";
expressions: [ Expression ];
}Sequence 表达式
如1, 2, 3AssignmentExpressioninterface AssignmentExpression <: Expression {
type: "AssignmentExpression";
operator: AssignmentOperator;
left: Pattern | Expression;
right: Expression;
}赋值表达式节点
1.operator: 赋值运算符
2.left/right: 赋值运算符左右的表达式
3.left: ???ConditionalExpressioninterface ConditionalExpression <: Expression {
type: "ConditionalExpression";
test: Expression;
alternate: Expression;
consequent: Expression;
}条件表达式
就是三元运算表达式,即a > b ? c : dLogicalExpressioninterface LogicalExpression <: Expression {
type: "LogicalExpression";
operator: LogicalOperator;
left: Expression;
right: Expression;
}逻辑运算表达式
如a && bBinaryExpressioninterface BinaryExpression <: Expression {
type: "BinaryExpression";
operator: BinaryOperator;
left: Expression | PrivateName;
right: Expression;
}二元运算表达式
如a > b
1.left/right: 运算符左右的表达式
2.operator: 二元运算符节点
3.left: 还可以是PrivateName节点UnaryExpressioninterface UnaryExpression <: Expression {
type: "UnaryExpression";
operator: UnaryOperator;
prefix: boolean;
argument: Expression;
}一元表达式
如typeof 1UpdateExpressioninterface UpdateExpression <: Expression {
type: "UpdateExpression";
operator: UpdateOperator;
argument: Expression;
prefix: boolean;
}Update 表达式
如++a
1.operator:UpdateOperator节点,即++/--
2.argument: 参数
3.prefix: 表示operator是否在前OptionalMemberExpressioninterface OptionalMemberExpression <: Expression {
type: "OptionalMemberExpression";
object: Expression;
property: Expression | PrivateName;
computed: boolean;
optional: boolean;
}可选链表达式 a?.b?.cMemberExpressioninterface 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节点BindExpressioninterface BindExpression <: Expression {
type: "BindExpression";
object: Expression | null;
callee: Expression;
}TaggedTemplateExpressioninterface TaggedTemplateExpression <: Expression {
type: "TaggedTemplateExpression";
tag: Expression;
quasi: TemplateLiteral;
}带标签的模板字符串表达式
function tagFunc(...args) {
return args;
}
const setting = 'dark mode';
console.log(tagFunc`Setting ${ setting } is ${ value }!`);OptionalCallExpressioninterface OptionalCallExpression <: Expression {
type: "OptionalCallExpression";
callee: Expression;
arguments: [ Expression | SpreadElement ];
optional: boolean;
}可选执行方法节点
如f?.()()中的?.()CallExpressioninterface CallExpression <: Expression {
type: "CallExpression";
callee: Expression | Super | Import;
arguments: [ Expression | SpreadElement ];
}函数调用表达式
如run(1, 2)这样的表达式。
1.callee: 表示函数,是表达式节点
2.arguments: 表示参数,为Expression或SpreadElementImportinterface Import <: Node {
type: "Import";
}Import 表达式 import * from './a'ThisExpressioninterface ThisExpression <: Expression {
type: "ThisExpression";
}This表达式 thisPipelinePrimaryTopicReferenceDoExpressioninterface DoExpression <: Expression {
type: "DoExpression";
body: BlockStatement;
}do表达式 do { ... }Superinterface Super <: Node {
type: "Super";
}Super 表达式 super([arguments])PrivateNameinterface PrivateName <: Node {
type: "PrivateName";
id: Identifier;
}表示私有的标识符 MetaPropertyinterface MetaProperty <: Expression {
type: "MetaProperty";
meta: Identifier;
property: Identifier;
}元属性 new.targetStringLiteralinterface StringLiteral <: Literal {
type: "StringLiteral";
value: string;
}字符串字面量 NumericLiteralinterface NumericLiteral <: Literal {
type: "NumericLiteral";
value: number;
}数字字面量 BigIntLiteralinterface BigIntLiteral <: Literal {
type: "BigIntLiteral";
value: string;
}BigInt 字面量 DecimalLiteralinterface DecimalLiteral <: Literal {
type: "DecimalLiteral";
value: string;
}RegExpLiteralinterface RegExpLiteral <: Literal {
type: "RegExpLiteral";
pattern: string;
flags: string;
}正则字面量
1.pattern: 正则表达式内容字符,如/a/i中的"a"
2.flags:i、g等标志BooleanLiteralinterface BooleanLiteral <: Literal {
type: "BooleanLiteral";
value: boolean;
}布尔值字面量 NullLiteralinterface NullLiteral <: Literal {
type: "NullLiteral";
}null 字面量 SequenceExpressioninterface SequenceExpression <: Expression {
type: "SequenceExpression";
expressions: [ Expression ];
}Sequence 表达式
如1, 2, 3ParenthesizedExpressioninterface ParenthesizedExpression <: Expression {
type "ParenthesizedExpression";
expression: Expression;
}括号表达式 NewExpressioninterface NewExpression <: CallExpression {
type: "NewExpression";
}new表达式
如new Date()TemplateElementinterface TemplateElement <: Node {
type: "TemplateElement";
tail: boolean;
value: {
cooked: string | null;
raw: string;
};
}模板元素 TemplateLiteralinterface TemplateLiteral <: Expression {
type: "TemplateLiteral";
quasis: [ TemplateElement ];
expressions: [ Expression ];
}模板字面量 Hello ${name}ObjectExpressioninterface ObjectExpression <: Expression {
type: "ObjectExpression";
properties: [
ObjectProperty |
ObjectMethod |
SpreadElement
];
}Object表达式
如"{ a: 1 }"properties为数组,每个元素可以是:
1.ObjectProperty节点
2.ObjectMethod节点
3.SpreadElement节点ObjectPatterninterface AssignmentProperty <: ObjectProperty {
value: Pattern;
}
interface ObjectPattern <: Pattern {
type: "ObjectPattern";
properties: [ AssignmentProperty | RestElement ];
}RecordExpressioninterface RecordExpression <: Expression {
type: "RecordExpression";
properties: [
ObjectProperty |
ObjectMethod |
SpreadElement
];
}Record表达式,如 #{ a: 1, b: '2' }RestElementinterface RestElement <: Pattern {
type: "RestElement";
argument: Pattern;
}fun(…args) {}ObjectPropertyinterface ObjectProperty <: ObjectMember {
type: "ObjectProperty";
shorthand: boolean;
value: Expression;
}属性节点 TupleExpressioninterface TupleExpression <: Expression {
type: "TupleExpression";
elements: [ Expression | SpreadElement | null ];
}ArrayExpressioninterface ArrayExpression <: Expression {
type: "ArrayExpression";
elements: [ Expression | SpreadElement | null ];
}数组表达式
如"[1, 2, 3]"elements为数组,表示数组的多个元素,每个元素为表达式或nullArrowFunctionExpressioninterface ArrowFunctionExpression <:
Function, Expression {
type: "ArrowFunctionExpression";
body: BlockStatement | Expression;
}箭头函数表达式 () => {[body]}ArgumentPlaceholderinterface ArgumentPlaceholder <: Node {
type: "ArgumentPlaceholder";
}Identifierinterface Identifier <: Expression, Pattern {
type: "Identifier";
name: string;
}标识符
即自定义名称,如变量名、属性名、函数名
一个标识符可以是一个表达式(Expression),或者是解构(Pattern)AwaitExpressioninterface AwaitExpression <: Expression {
type: "AwaitExpression";
argument: Expression | null;
}Await 表达式 await [argument]YieldExpressioninterface YieldExpression <: Expression {
type: "YieldExpression";
argument: Expression | null;
delegate: boolean;
}Yield 表达式 yield [argument]PipelineBareFunctionPipelineTopicExpressionModuleExpression校验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 )的各种处理。
生成的左值节点
分类 节点名称 定义 描述 SpreadElementinterface SpreadElement <: Node {
type: "SpreadElement";
argument: Expression;
}Spread 表达式
如[a, …b]RestElementinterface RestElement <: Pattern {
type: "RestElement";
argument: Pattern;
}fun(…args) {}ArrayPatterninterface ArrayPattern <: Pattern {
type: "ArrayPattern";
elements: [ Pattern | null ];
}[ name, age ] = [ 'Jack', 13 ]AssignmentPatterninterface 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,使用时重命名为Errorsbabel-parser/src/parser/error-message.js的SourceTypeModuleErrorMessages,使用时重命名为SourceTypeModuleErrorsbabel-parser/src/plugins/jsx/index.js的JsxErrors,使用时继续用JsxErrors
以下是 Babel 在解析 AST 节点过程中会抛出的错误。
# 语句类错误(statement.js)
export的对象未定义Errors.ModuleExportUndefinedExport '%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.StrictFunctionIn 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.SloppyFunctionIn 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.UnexpectedLexicalDeclarationLexical declaration cannot appear in a single-statement context.1以下案例中:
if (true) let a = 11这个案例的转译报错并不是这个,而是
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.AsyncFunctionInSingleStatementContextAsync functions can only be declared at the top level or inside a block.1这种写法的源码在转译时会触发该报错:
if (true) async function f() { }1装饰器必须作用于类的声明中
Errors.UnexpectedLeadingDecoratorLeading 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或continueErrors.IllegalBreakContinueUnsyntactic %0.1表示非法的
break或continue。这两个词法单元只会作用于循环,如果没有写在循环里,转译时就会发现并且报错。if (true) { break; }1会报错
Unsyntactic break。if (true) { continue; }1会报错
Unsyntactic continue。Errors.ForOfLetThe left-hand side of a for-of loop may not start with 'let'.1TODO
Errors.ForOfAsyncThe left-hand side of a for-of loop may not be 'async'.1TODO
非法的
returnErrors.IllegalReturn'return' outside of function.1转译时,函数体外出现了
return会抛出此错误。比如:
return1但如果这样配置,就允许函数体外出现
return:babel.transformSync(code, { parserOpts: { allowReturnOutsideFunction: true } })1
2
3
4
5switch语句中是否有多余的defaultErrors.MultipleDefaultsInSwitchMultiple default clauses.1switch(name) { case 'Jack': break; default: break; default: break; }1
2
3
4
5
6
7
8throw不允许出现换行符Errors.NewlineAfterThrowIllegal newline after throw.1throw 'error'1
2Errors.NoCatchOrFinallyMissing catch or finally clause.1try { console.log('Hi') }1
2
3Errors.StrictWith'with' in strict mode.1'use strict'; with1
2Errors.LabelRedeclarationLabel '%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.InvalidLhsInvalid left-hand side in %0.11 = 11SyntaxError: 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.GeneratorInSingleStatementContextGenerators can only be declared at the top level or inside a block.1已知,在非严格模式下,这样的写法是合法的:
if (true) function run() {}1但 Generator 函数例外,这样的写法是非法的:
if (true) function* run() {}1Errors.DecoratorSemicolonDecorators 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.TrailingDecoratorDecorators 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.ConstructorIsGeneratorConstructor 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.DuplicateConstructorDuplicate 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.ConstructorIsAsyncConstructor 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.ConstructorIsAccessorClass constructor may not be an accessor.1TODO
Errors.StaticPrototypeClasses 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.ConstructorClassPrivateFieldClasses 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.DecoratorStaticBlockDecorators 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.ConstructorClassFieldClasses 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.DecoratorBeforeExportDecorators 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.UnsupportedDefaultExportOnly expressions, functions or classes are allowed as the `default` export.1export default const x = 11SyntaxError: 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.ExportBindingIsStringA string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '%0' as '%1' } from 'some-module'`?1TODO
Errors.UnsupportedDecoratorExportA decorated export must export a class declaration.1TODO
Errors.DuplicateDefaultExportOnly one default export allowed per module.1export default x export default y1
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.ModuleExportNameHasLoneSurrogateAn export name cannot include a lone surrogate, found '\\u%0'.1TODO
Errors.ModuleAttributesWithDuplicateKeysDuplicate key "%0" is not allowed in module attributes.1TODO
Errors.ModuleAttributeDifferentFromTypeThe only accepted module attribute is `type`.1TODO
Errors.DestructureNamedImportES2015 named imports do not destructure. Use another statement for destructuring after the import.1TODO
Errors.ImportBindingIsStringA 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.ImportMetaOutsideModuleimport.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.DuplicateProtoRedefinition of __proto__ property.1TODO
Errors.UnexpectedAwaitAfterPipelineBodyUnexpected "await" after pipeline body; await must have parentheses in minimal proposal.1TODO
Errors.MixingCoalesceWithLogicalNullish coalescing operator(??) requires parens when mixing with logical operators.1TODO
Errors.UnexpectedTokenUnaryExponentiationIllegal expression. Wrap left hand side or entire exponentiation in parentheses.1Errors.StrictDeleteDeleting local variable in strict mode.1Errors.DeletePrivateFieldDeleting a private field is not allowed.1Errors.SuperPrivateFieldPrivate fields can't be accessed on super.1Errors.OptionalChainingNoTemplatehttps://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.ImportCallArgumentTrailingCommaTrailing 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.UnsupportedBindBinding should be performed on object property.1Errors.PrivateInExpectedInPrivate names are only allowed in property accesses (`obj.#%0`) or in `in` expressions (`#%0 in obj`).1Errors.PrimaryTopicRequiresSmartPipelinePrimary Topic Reference found but pipelineOperator not passed 'smart' for 'proposal' option.1Errors.PrimaryTopicNotAllowedTopic reference was used in a lexical context without topic binding.1Errors.LineTerminatorBeforeArrowNo 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.UnexpectedPrivateFieldPrivate 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.UnsupportedMetaPropertyThe only valid meta property for %0 is %0.%1.1import.name1SyntaxError: 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.target1SyntaxError: unknown: `new.target` can only be used in functions or class properties. (1:0) > 1 | new.target | ^1
2
3
4Errors.ImportCallNotNewExpressionCannot use new with import(...).1new import(x)1SyntaxError: unknown: Cannot use new with import(...). (1:4) > 1 | new import(x) | ^1
2
3
4Errors.OptionalChainingNoNewConstructors 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.InvalidEscapeSequenceTemplateInvalid escape sequence in template.1Errors.InvalidRecordPropertyOnly properties and spread elements are allowed in record definitions.1Errors.UnsupportedPropertyDecoratorDecorators cannot be used to decorate object literal properties.1Errors.AccessorIsGeneratorA %0ter cannot be a generator.1Errors.BadSetterRestParameterA 'set' accesor function argument must not be a rest parameter.1Errors.IllegalLanguageModeDirectiveIllegal 'use strict' directive in function with non-simple parameter list.1Errors.UnexpectedTokenUnexpected token '%0'.1Errors.UnexpectedArgumentPlaceholderUnexpected argument placeholder.1Errors.YieldBindingIdentifierCan 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.AwaitBindingIdentifierCan 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.AwaitBindingIdentifierInStaticBlockCan not use 'await' as identifier inside a static block.1Errors.ArgumentsInClass'arguments' is only allowed in functions and class methods.1Errors.UnexpectedKeywordUnexpected keyword '%0'.1Errors.UnexpectedReservedWordUnexpected reserved word '%0'.1Errors.ObsoleteAwaitStar'await*' has been removed from the async functions proposal. Use Promise.all() instead.1Errors.PipelineHeadSequenceExpressionPipeline head should not be a comma-separated sequence expression.1Errors.PipelineBodySequenceExpressionPipeline body may not be a comma-separated sequence expression.1Errors.PipelineTopicUnusedPipeline is in topic style but does not use topic reference.1
# 左值类错误(lval.js)
Errors.InvalidParenthesizedAssignmentInvalid 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.MissingEqInAssignmentOnly '=' operator can be used for specifying default value.1Errors.UnsupportedParameterDecoratorDecorators 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.ParamDupeArgument 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 = 11SyntaxError: 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.InvalidPropertyBindingPatternBinding member expression.1Errors.InvalidLhsInvalid 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.InvalidLhsBindingBinding 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.InvalidRestAssignmentPatternInvalid rest operator's argument.1Errors.ElementAfterRestRest 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.RestTrailingCommaUnexpected 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.jsErrors.InvalidPrivateFieldResolutionPrivate name #%0 is not defined.1Errors.PrivateNameRedeclarationDuplicate private name #%0.1
scope.jsErrors.VarRedeclarationIdentifier '%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.SyntaxErrorBABEL_PARSER_SYNTAX_ERROR1Errors.UnterminatedStringUnterminated 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.UnterminatedJsxContentUnterminated 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.AttributeIsEmptyJSX 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.UnsupportedJsxValueJSX 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.UnexpectedSequenceExpressionSequence expressions cannot be directly nested inside JSX. Did you mean to wrap it in parentheses (...)?1JsxErrors.MissingClosingTagFragmentExpected corresponding JSX closing tag for <>.1JsxErrors.UnwrappedAdjacentJSXElementsAdjacent 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-objectclass 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-applicationf(x, ?) // partial application from left f(?, x) // partial application from right f(?, x, ?) // partial application for any arg1
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 map1
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-methodsclass 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-fromexport v from "mod";1moduleStringNames语法插件:
babel-plugin-syntax-module-string-names开启对
moduleStringNames语法的支持,也就是模块别名的支持。export { hi as 'hello' } from './hi.js';1moduleAttributesdecimal语法插件:
babel-plugin-syntax-demical
# 总结
TODO