跳过内容

规范

ECMAScript® 2023 语言规范详细介绍了 JavaScript 语言的方方面面,这样任何人都可以实现自己的 JavaScript 引擎。

以下章节需要为我们的解析器学习

  • 第 5 章:符号约定
  • 第 11 章:ECMAScript 语言:源代码
  • 第 12 章:ECMAScript 语言:词法语法
  • 第 13 - 16 章:表达式、语句、函数、类、脚本和模块
  • 附录 B:Web 浏览器其他 ECMAScript 功能
  • 附录 C:ECMAScript 的严格模式

用于规范内的导航

  • 所有可点击内容均设有永久链接,它们在 URL 中显示为锚点,例如 #sec-identifiers
  • 将鼠标悬停在内容上可能会显示一个提示框,点击“参考文献”可以显示其全部参考文献

符号约定

第 5.1.5 章 语法符号是我们需要阅读的部分。

此处需要注意的方面有

递归

此部分介绍了如何在语法中表示列表。

ArgumentList :
  AssignmentExpression
  ArgumentList , AssignmentExpression

意为

javascript
a, b = 1, c = 2
^_____________^ ArgumentList
   ^__________^ ArgumentList, AssignmentExpression,
          ^___^ AssignmentExpression

可选

用于可选语法的后缀为 _opt_。例如:

VariableDeclaration :
  BindingIdentifier Initializer_opt

意为

javascript
var binding_identifier;
var binding_identifier = Initializer;
                       ______________ Initializer_opt

参数

语法中的参数包括 [Return][In]

例如:

ScriptBody :
    StatementList[~Yield, ~Await, ~Return]

意味着顶级生成器、await 和 return 在脚本中不被允许,但

ModuleItem :
  ImportDeclaration
  ExportDeclaration
  StatementListItem[~Yield, +Await, ~Return]

允许顶级 await。

源文本

第 11.2 章源代码类型 告诉我们,脚本代码和模块代码之间有很大差别。并且有一个 use strict 模式,它通过禁止旧的 JavaScript 行为来使语法更合理。

脚本代码不严格,需要在文件顶部插入 use strict 使脚本代码严格。在 HTML 中,我们编写 <script src="javascript.js"></script>

模块代码自动严格。在 HTML 中,我们编写 <script type="module" src="main.mjs"></script>

ECMAScript 语言: 词法语法

有关更深入的解释,请阅读 V8 博客,了解 理解 ECMAScript 规范

自动插入分号

本节描述了在编写 JavaScript 时我们可以在何处省略分号的所有规则。所有解释都归结为

rust
    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 函数,例如在语句末尾

rust
    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))
    }

信息

本节关于 asi 的内容是针对编写解析器而写的,它明确指出源文本是从左到右进行解析的,这使得几乎不可能用任何其他方式编写解析器。jsparagus 的作者对此发表了一篇批评文章 在此

此功能的规范一方面非常高层,另一方面又奇异地带有程序性(“当在从左到右解析源文本时,遇到令牌...”,仿佛该规范在讲述浏览器的故事。据我所知,这是规范中唯一假定或暗示解析内部实现细节的地方。)但是很难用其他方式指定 ASI。

表达式、语句、函数、类、脚本和模块

理解语法需要一段时间,然后运用这些语法编写解析器。

根据MIT许可证发布。