V8 Blog: Understanding the ECMAScript spec, part 3
https://v8.dev/blog/understanding-ecmascript-part-3
↓ECMAScript仕様輪読会 #4の振り返り会で読んだときのメモです。
ECMAScriptの仕様で定義されている文法は4つ
lexical grammar: 字句解析用の文法。入力列をトークン列とかにしてくれる。
syntactic grammar: トークン列が構文的に妥当なのか確認する文法
RegExp grammar: 正規表現リテラルとかをパースするための文法
numeric string grammar: ソースコードの1234, 0xffffとかの数値を表す文字列
これらはすべてContext-Free Grammars(文脈自由文法)として定義されている。
Lexical Grammar
is 何?
ソースコードをトークン列にするための文法
(Context-Freeって言ってたけど)実際には文脈に依存したパース処理が必要になる
例: /の扱い
parserがgoal symbolをlexerに渡し分けることで、文脈に応じたパース処理を実現している
ECMAScriptのソース文はUnicodeコードポイント列として定義されている
実際のソースコードのエンコードは仕様では気にしない
最終的にUnicodeコードポイント列になっていればOK
goal symbols:
Starting from a sentence consisting of a single distinguished nonterminal, called the goal symbol,
distinguished: どう訳せばいいんだろう?
他では使われていないたった一つの非終端記号
https://stackoverflow.com/questions/45922001/is-goal-symbol-the-same-thing-as-start-symbol-in-context-free-grammar
(英語) is not an option here: 選択肢にならない
Syntactic grammar
is 何?
lexical grammarで作ったトークン列を解釈するための文法
JavaScriptに新しいキーワードを導入するとき、どんなふうに文法を定義するか
新しいキーワードを導入することで古いコードが文法エラーにならないように、うまく文法を定義する必要がある
新キーワードawaitの例
async関数の中だけでキーワードになるような文法を定義した
こういう定義
code:grammar
VariableStatementYield, Await :
var VariableDeclarationList+In, ?Yield, ?Await ;
文法定義の中でshorthandsを使ってる
左辺
[Yield, Await]: 展開すると以下になる
VariableStatement
VariableStatement_Yield
VariableStatement_Await
VariableStatement_Yield_Await
右辺
[+In]: すべての展開結果に_Inを付加する
これ使わずにVariableDeclarationList_In[?Yield, ?Await]って書いたらダメなんだろうかyebis0942.icon
[?Yield]: 左辺に_Yieldがあれば右辺も_Yieldを付加する
つまり_Yieldを左辺から右辺に引き継ぐ
[~Foo]: _Fooを付加しない
VariableStatement_Await: awaitをキーワード扱いするバージョンのVariableStatement
async functionではVariableStatement_Awaitが使われるようにする
awaitはどのように禁止されているのか
こんな感じの定義なのかな?
BindingIdentifierの左辺にはawaitがあって
BindingIdentifier_Awaitの左辺にはawaitがない
実はそうではない!
BindingIdentifier_Awaitの左辺にもawaitはあります
なので文法生成規則のうえでは文法エラーにならない
別途static semanticsを使って文法エラーにしている
「BindingIdentiferがAwaitパラメータを持っているとき、awaitはSyntax Errorとする」というルールが明記されている
なぜstatic semanticsを使っているのか
文法生成規則によるパースに失敗するとセミコロン自動挿入(ASI)が実行されるという仕様がある
あまりにも理解しづらい挙動なのでawaitまわりではASIさせたくない
文法生成規則ではパースに成功させつつ、static semanticsで文法エラーにすることにした
識別子のStringValues対策
var \u0061wait;でawaitという変数を定義できる
\u0061waitはlexical grammarではIdentifier扱い
awaitはReservedWordなので弾けるのだが…
でもStringValueはawaitになる
これも別のstatic semanticsで明示的にSyntax Errorにしている
lexical grammarで処理する前に\u00xxをうまく片付けられないんだろうか?yebis0942.icon
コード内にUnicodeエスケープシーケンスを書けて嬉しいケースはあるんだろうか?yebis0942.icon
The static semantics for forbid using it as a variable name inside async functions.
これ訳せないねって話をしてたのですが、forを取り除けば正しく解釈できる気がするyebis0942.icon
正しそうです。ただsemanticsが単数の一方でforbidが三人称単数でないのが気になっていますnoyan.icon
これも含めてtypoでしょうか?
まとめ
lexical grammar(字句文法), syntactic grammar(構文文法), syntactic grammarの省略記法を紹介しました
例としてawaitをasync関数の中だけでキーワードとして扱う方法について紹介しました
セミコロン自動挿入やカバー文法については今後の記事で紹介します。おたのしみに!