Slide 1

Slide 1 text

1BSTJOH+BWBTDSJQU

Slide 2

Slide 2 text

Who Name 青野健利 GitHub @brn Twitter @brn227 What Dev Lead at 株式会社AI Shift Contributor of V8 Javascript Engine

Slide 3

Slide 3 text

8IBUJTQBSTJOH

Slide 4

Slide 4 text

Parseとは What is parsing 文字列で記述されたプログラミング言語を 構造化された状態に変換すること

Slide 5

Slide 5 text

const a = "Hello World" 7BSJBCMF%FDMBSBUJPO *EFOUJ fi FS B -JUFSBM l)FMMP8PSMEz

Slide 6

Slide 6 text

Parseの流れ What is parsing 1. ソースコードの読み出し 2. Unicode Sequenceや各種のEscapeを実際の値に変換 3. Token化 4. Parse

Slide 7

Slide 7 text

各種Escapeのdecode What is parsing Javascriptには以下のEscape Sequenceがあるので Decodeして文字列化しておく 1. Unicode Sequence 2. Hex Escape 3. Ascii Escape 4. Octal Literal 5. Binary Literal "\u0056" 0xFFFFFF "\x12" 0777 0b0000001

Slide 8

Slide 8 text

字句解析 What is parsing ソースコードの文字列そのままでParseするのはしんどいので ソースコードからTokenへと変換する

Slide 9

Slide 9 text

Token化 What is parsing KEYWORD(const) IDENTIFIER(a) OPERATOR(eq) STRING_LITERAL(Hello World) const a = "Hello World"

Slide 10

Slide 10 text

構文解析 What is parsing Token列を処理する

Slide 11

Slide 11 text

BNF What is parsing Parse方法について話す前にBNFについて BNFとはバッカス・ナウア・フォーム の略でプログラミング言語の文法を表すのに利用される

Slide 12

Slide 12 text

BNF What is parsing ::= | "+" | "-" ::= | "*" | "/" ::= | "(" ")" ::= | ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" このBNFは単純な算術演算("2*(3+4)" や “5-3/2”のような)を表している

Slide 13

Slide 13 text

BNF What is parsing 多くのプログラミング言語はBNFによる文法の定義を持っている Ecmascriptも例外ではなく ECMA262の仕様書のAppendix A Grammar Summary というページが存在し、そこにBNFで文法が定義されている https://262.ecma-international.org/14.0/#sec-grammar-summary

Slide 14

Slide 14 text

Parse手法 What is parsing プログラミング言語をParseする方法はいくつか種類がある

Slide 15

Slide 15 text

Top-Down Parsing What is parsing Top-Down Parsing • 再帰下降構文解析 • LL(N)

Slide 16

Slide 16 text

再帰下降構文解析 What is parsing それぞれの文法をParseするための関数を 再帰的に呼び出すことでParseする手法 手書き向き

Slide 17

Slide 17 text

LL(N) What is parsing Left To RightでParseし、左端導出というものを行う

Slide 18

Slide 18 text

左端導出 What is parsing const a = "Hello World" 7BSJBCMF%FDMBSBUJPO.PEJ fi FS*EFOUJ fi FS&YQS PNJU .PEJUJ fi FSlDPOTUzclMFUz *EFOUJ fi FS$IBS PNJU

Slide 19

Slide 19 text

const a = "Hello World" 7BSJBCMF%FDMBSBUJPO const a = "Hello World" .PEJUJ fi FS const a = "Hello World" .PEJUJ fi FS *EFOUJ fi FS lDPOTUz const a = "Hello World" .PEJUJ fi FS *EFOUJ fi FS lDPOTUz $IBS PNJU B

Slide 20

Slide 20 text

const a = "Hello World" .PEJUJ fi FS *EFOUJ fi FS lDPOTUz $IBS PNJU B &YQS PNJU const a = "Hello World" .PEJUJ fi FS *EFOUJ fi FS lDPOTUz $IBS PNJU B &YQS PNJU 4USJOH-JUFSBM 0NJU l)FMMP8PSMEz

Slide 21

Slide 21 text

const a = "Hello World" .PEJUJ fi FS *EFOUJ fi FS lDPOTUz $IBS PNJU B &YQS PNJU 4USJOH-JUFSBM 0NJU l)FMMP8PSMEz 7BSJBCMF%FDMBSBUJPO

Slide 22

Slide 22 text

Bottom-Up Parsing What is parsing Bottom-Up Parsing • LR(N) • LALR(N)

Slide 23

Slide 23 text

LR法 What is parsing Left To RightでParseし、右端導出を行う 手書きでは作成が難しく BisonやYacc等のParserジェネレータを使い 状態遷移テーブルから次の状態を決め 最終的な文法が決まる

Slide 24

Slide 24 text

右端導出 What is parsing const a = "Hello World" 7BSJBCMF%FDMBSBUJPO.PEJ fi FS*EFOUJ fi FS&YQS PNJU .PEJUJ fi FSlDPOTUzclMFUz *EFOUJ fi FS$IBS PNJU

Slide 25

Slide 25 text

const a = "Hello World" .PEJUJ fi FS 4 const a = "Hello World" .PEJUJ fi FS 3 *EFOUJ fi FS 4 lDPOTUz const a = "Hello World" .PEJUJ fi FS 3 *EFOUJ fi FS 3 lDPOTUz $IBS PNJU B < DPOTU 4 4FYQFDUFE> <.PEJ fi FS B 4 4FYQFDUFE> <.PEJ fi FS *EFOUJ fi FS 4>

Slide 26

Slide 26 text

const a = "Hello World" .PEJUJ fi FS 3 *EFOUJ fi FS 3 lDPOTUz $IBS PNJU B &YQS PNJU 4 const a = "Hello World" .PEJUJ fi FS 3 *EFOUJ fi FS 3 lDPOTUz $IBS PNJU B &YQS PNJU 3 4USJOH-JUFSBM PNJU 3 l)FMMP8PSMEz <.PEJ fi FS *EFOUJ fi FS 4> <.PEJ fi FS *EFOUJ fi FS &YQS>

Slide 27

Slide 27 text

const a = "Hello World" .PEJUJ fi FS 3 *EFOUJ fi FS 3 lDPOTUz $IBS PNJU B &YQS PNJU 3 4USJOH-JUFSBM PNJU 3 l)FMMP8PSMEz <7BSJBCMF%FDMBSBUJPO>

Slide 28

Slide 28 text

結局? What is parsing Javascriptは一応LL(1)の文法なので 再帰下降構文解析を使うことが多い メジャーなJavascriptエンジンは手書きで書いている

Slide 29

Slide 29 text

結局? What is parsing LRの構文解析器を作成するためには BNFレベルで文法の曖昧さをなくしていく必要があるが JavascriptはBNFレベルでは厳密なパースができないので むしろ手書き以外だと非常に面倒なことになる

Slide 30

Slide 30 text

1BSTJOH+BWBTDSJQU

Slide 31

Slide 31 text

理想と現実 Parsing Javascript 理想: ParserとScannerが疎結合かつ単純なパーサジェネレー タでパーサが作れる 現実: ParserとScannerは相互に結合し、手書きで泥臭いコー ドを大量に書いて、ようやくパースできても重箱の隅的な言語仕 様に引っかかり、さらに泥臭いコードを書いて解決する

Slide 32

Slide 32 text

ここがつらいよJavascript Parsing Javascript 正規表現

Slide 33

Slide 33 text

正規表現がつらい Parsing Javascript 正規表現リテラルは/で始まり/で終わる Scannerにとって/をどう解釈するかがキモ

Slide 34

Slide 34 text

正規表現がつらい Parsing Javascript なので、Parser側で正規表現が来ることが可能な箇所を Parserの状態として持っておいて、Scannerから参照すること で、”/”の処理を変える必要がある

Slide 35

Slide 35 text

テンプレートリテラルがつらい Parsing Javascript テンプレートリテラルも同様 “`”の処理を考える必要がある

Slide 36

Slide 36 text

ここがつらいよJavascript Parsing Javascript 分割代入

Slide 37

Slide 37 text

分割代入がつらい Parsing Javascript 分割代入は左辺に特殊な形式のObject/Array Literal を利用することが可能だが、ParserはParseのタイミングでそれ がArray/Objectかどうかを判断するのが難しい

Slide 38

Slide 38 text

分割代入がつらい Parsing Javascript [a,b,c] = [1,2,3] [a,b,c]をパースする時点ではそれが何かはわからない

Slide 39

Slide 39 text

分割代入がつらい Parsing Javascript なので分割代入用に拡張されたArray/Object のASTを用意して、先にParseしてから親の文法側で左辺に Array/Objectが来ていた場合だけValidationを実施する

Slide 40

Slide 40 text

({a = 1}) = {a} ObjectLikeLiteral { Property { has_assignment = true Identifier {a} } } ({a = 1}) = {a} ObjectLikeLiteral { Property { has_assignment = true Identifier {a} } } Validationを実施 has_assignmentは許可される

Slide 41

Slide 41 text

({a = 1}) ObjectLikeLiteral { Property { has_assignment = true Identifier {a} } } ({a = 1}) ObjectLikeLiteral { Property { has_assignment = true Identifier {a} } } Validationを実施 has_assignmentは許可されないので エラー

Slide 42

Slide 42 text

ここがつらいよJavascript Parsing Javascript Arrow Function

Slide 43

Slide 43 text

ArrowFunctionがつらい Parsing Javascript アロー関数は簡潔に書けるので非常に良いのだが Parserは地獄

Slide 44

Slide 44 text

ArrowFunctionがつらい Parsing Javascript (a, b, c = 1) => {} (a, b, c = 1) この2つを左からParseして別の構文木として作り上げる必要がある そのため、Ecma262では CoverCallExpressionAndAsyncArrowHead というExpressionとArrowFunction両方をカバーするための文法規則が導入されている

Slide 45

Slide 45 text

ArrowFunctionがつらい Parsing Javascript アロー関数も基本的には分割代入とかと同じで、まずは ExpressionとしてParseして、”=>”が見えたらアロー関数とし て取り扱うことでなんとか回避する

Slide 46

Slide 46 text

ここがつらいよJavascript Parsing Javascript 他にも色々あるけど割愛 - Eary Errors - Strict Mode etc..

Slide 47

Slide 47 text

1SF1BSTJOH

Slide 48

Slide 48 text

PreParsingとは PreParsing ブラウザで動くJavascriptならではの工夫が PreParsing

Slide 49

Slide 49 text

PreParsingとは PreParsing 事前に関数の外観だけParseして呼び出されるまでParse自体 をスキップする機能 すべての関数定義をParseしているとStartup Timeが遅くなる ので先に今からすぐ動かすべきポイントだけParseして動作させ る

Slide 50

Slide 50 text

PreParsingとは PreParsing function A() { alert(1) } function B () { alert(2) } document.addEventListener("#target", "click", () => { A() B() }) 関数AとBはParseされずに document.addEventListenerがParseされる AとBはclickが発生したタイミングでParseされる

Slide 51

Slide 51 text

どのように実現するか PreParsing ブラウザごとに実現方法が違うので紹介

Slide 52

Slide 52 text

Chrome PreParsing PreParserという別のParser実装が存在しており、Parse処理 自体を変更している 関数を見つけると引数等の情報のみParseしてBodyはすべて ParseはするもののASTを生成せずにスキップする処理に差し替 えてある

Slide 53

Slide 53 text

Webkit PreParsing AST生成側に工夫があり、PreParsingのタイミングではASTは 空のオブジェクトのみを返し、新たに値を生成しない

Slide 54

Slide 54 text

どっち PreParsing どっちが良いというものはないが、自分が実装したときは Webkit方式を採用した

Slide 55

Slide 55 text

1BSTFS5FTU

Slide 56

Slide 56 text

テスト PreParsing Parserを自分で書いた場合テストがかなり大変だが、 Ecmaがテストケースを用意していくれており https://github.com/tc39/test262-parser-tests に必要なテストケースが大量にある

Slide 57

Slide 57 text

͓ΘΓ