Skip to content

仕様

ECMAScript® 言語仕様(リビングスタンダード)は JavaScript 言語のすべてを定義しており、誰でも独自エンジンを実装できます。

パーサ実装のために読むべき章は次のとおりです。

  • 第 5 章: 表記法
  • 第 11 章: ECMAScript 言語: ソーステキスト
  • 第 12 章: ECMAScript 言語: 字句文法
  • 第 13〜16 章: 式、文、関数、クラス、スクリプトとモジュール
  • 付録 B: Web ブラウザ向けの追加 ECMAScript 機能
  • 付録 C: ECMAScript の厳格モード

仕様内のナビゲーション:

  • クリック可能な箇所には恒久リンクがあり、URL のアンカーに現れます(例: #sec-identifiers
  • ホバーでツールチップが出ることがあり、References をクリックすると参照が一覧できます

表記法

第 5.1.5 節 Grammar Notation を読む必要があります。

ここで押さえる点:

再帰

文法中でリストがこのように示されます。

ArgumentList :
  AssignmentExpression
  ArgumentList , AssignmentExpression

これは次を意味します。

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

任意(Optional)

_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 節 Types of Source Code では、スクリプトコードとモジュールコードの大きな区別と、use strict によって古い挙動を禁じて文法をすっきりさせるモードが説明されます。

スクリプトコードは既定では厳格ではなく、ファイル先頭に use strict が必要です。 HTML では <script src="javascript.js"></script> のように書きます。

モジュールコードは自動的に厳格です。 HTML では <script type="module" src="main.mjs"></script> のように書きます。

ECMAScript 言語: 字句文法

より深くは V8 ブログの Understanding the ECMAScript spec を参照してください。

自動セミコロン挿入(ASI)

セミコロンを省略できる規則がこの節にまとまっています。要点は実装では次のとおりです。

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

INFO

ASI の節はパーサ実装者向けに書かれており、ソーステキストを左から右へパースすると明示されています。そのためパーサを別の書き方にするのはほぼ不可能です。jsparagus の作者は ここ でこれを指摘しています。

この機能の仕様は非常に高レベルで、奇妙に手続き的(「ソーステキストが左から右にパースされるときに、トークンが…」まるでブラウザの物語のよう。パースの内部実装を前提にしている箇所は仕様の中でここだけだと思う)。でも ASI を別の書き方で指定するのは難しい。

式、文、関数、クラス、スクリプトとモジュール

構文文法を理解してパーサに落とし込むまでには時間がかかります。