规范
《ECMAScript® 语言规范》(持续演进的活文档)囊括了 JavaScript 语言的全部语义,任何人都可据此实现引擎。
为我们的解析器需要先阅读以下章节:
- 第 5 章:符号约定
- 第 11 章:ECMAScript 语言:源代码
- 第 12 章:ECMAScript 语言:词法文法
- 第 13–16 章:表达式、语句、函数、类、脚本与模块
- Annex B:面向 Web 浏览器的附加 ECMAScript 特性
- Annex C:ECMAScript 的严格模式
在规范中转跳时请记住:
- 可点击条目都有锚点链接,形如
#sec-identifiers。 - 鼠标悬停可看到提示;点开
References可列出所有引用。
符号约定
务必阅读 Chapter 5.1.5 Grammar Notation。
本节重点:
递归
文法中用递归表示列表,例如:
ArgumentList :
AssignmentExpression
ArgumentList , AssignmentExpression对应语义:
a, b = 1, c = 2
^_____________^ ArgumentList
^__________^ ArgumentList, AssignmentExpression,
^___^ AssignmentExpression可选后缀 _opt_
前缀 _opt_ 表示片段可选:
VariableDeclaration :
BindingIdentifier Initializer_opt即可能存在也可能不存在 Initializer:
var binding_identifier;
var binding_identifier = Initializer;
______________ Initializer_opt参数:[Yield]、[In]、[Return]……
这些是文法规则的「参数」,例如:
ScriptBody :
StatementList[~Yield, ~Await, ~Return]顶层脚本禁止顶层 yield、await、return。而
ModuleItem :
ImportDeclaration
ExportDeclaration
StatementListItem[~Yield, +Await, ~Return]在模块语义下允许顶层 await。
源代码类型
第 11.2 章 Types of Source Code 说明 脚本代码与模块代码有本质差别;也可用 use strict 禁止旧时代的怪异行为以获得更清爽的语法表面。
Script Code并非默认严格——需要在文件头写 use strict 才可进入严格模式。
HTML 中写作 <script src="javascript.js"></script>。
Module Code 自动处于严格模式。
HTML 中写作 <script type="module" src="main.mjs"></script>。
ECMAScript 语言:词法文法
更深入的理解可参考 V8 博客 Understanding the ECMAScript spec。
Automatic Semicolon Insertion
本节描述所有可以省略分号的情形,核心可归约为:
pub fn asi(&mut self) -> Result<()> {
if self.eat(Kind::Semicolon) || self.can_insert_semicolon() {
return Ok(());
}
let range = self.prev_node_end..self.cur_token().start;
Err(SyntaxError::AutoSemicolonInsertion(range.into()))
}
pub const fn can_insert_semicolon(&self) -> bool {
self.cur_token().is_on_new_line || matches!(self.cur_kind(), Kind::RCurly | Kind::Eof)
}asi() 须在适用位置手动调用,例如语句末尾:
fn parse_debugger_statement(&mut self) -> Result<Statement<'a>> {
let node = self.start_node();
self.expect(Kind::Debugger)?;
self.asi()?;
self.ast.debugger_statement(self.finish_node(node))
}INFO
此节行文完全站在「从左向右解析源代码」的实现视角, 几乎决定了 Parser 的实现形态。jsparagus 作者对此有过吐槽 见此。
ASI 的规范措辞既高层次又出人意料地像是过程式叙述(「当源代码从左向右解析时遇到……」),仿佛在给浏览器讲故事。据我所知这是规范唯一暗示解析器内部实现细节的地方——想换一种表述也很难。
Expressions, Statements, Functions, Classes, Scripts and Modules
熟练掌握句法文法尚需时日,但一旦理解,就能把规则映射到实际的 Parser 实现里。