跳至內容

規範

ECMAScript® 2023 語言規範 詳細說明了 JavaScript 語言的所有內容,因此任何人都可以實作自己的 JavaScript 引擎。

為了我們的解析器,以下章節需要研讀

  • 第 5 章:符號約定
  • 第 11 章:ECMAScript 語言:原始文字
  • 第 12 章:ECMAScript 語言:詞法語法
  • 第 13 - 16 章:表達式、語句、函式、類別、腳本和模組
  • 附錄 B:網頁瀏覽器的其他 ECMAScript 功能
  • 附錄 C:ECMAScript 的嚴格模式

為了在規範內導覽

  • 任何可點擊的內容都有永久連結,它們在 URL 上顯示為錨點,例如 #sec-identifiers
  • 將滑鼠懸停在項目上可能會顯示工具提示,點擊 References 會顯示其所有參考文獻

符號約定

第 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]

表示在腳本中不允許頂層 yield、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 授權發布。