JavaScriptによるJavaScript実装
はじめに
- セルフホスティング
- ある言語でその言語自身の処理系を作ること
- 例:Cコンパイラ
- スクリプト言語でも一応行われている
- 例:PyPy(PythonによるPython実装)
JavaScriptでもセルフホスティングの前例はある
- Narcissus (Brendan Eichによる実装。)
でもSpiderMonkeyの開発ブランチでしか動かない
→じゃあ作るぜ
JavaScript概要
- 動的型付け
- 型宣言要らない
- プロトタイプベース
- オブジェクト指向の宗派の一つ。構文としてのクラスがない
- JavaScriptの他にはSelf,Lua,Io,NewtonScriptぐらいしかない
- 関数がファーストクラスなオブジェクト
- 数値や文字列といった値と同等に扱える
実装の目標と現状
目標
ECMA262 3rd edition準拠
ECMA262は各ベンダーのJSの共通部分の規格。JavaScript1.5, JScript5.5相当
現状
文法の実装がだいたい終わったところ
- まだfor in文と例外処理が実装されてない
- 組込オブジェクトの実装ができてない
ソースコードから実行までの流れ
- 字句解析
- ソースコードの文字列をトークンという単位に切り分ける
- 構文解析
- トークンの配列から構文木を作る
- 実行コード生成
- 構文木から実行コード(機械語やバイトコード)を作る
↓
実行コードを走らせる
字句解析
- トークン
- プログラム的に意味のある文字列の最小単位。
-
- 記号 + - { ) ++ など
- リテラル 3.14(数値リテラル) "hello"(文字列リテラル) など値を表す文字列
- 予約語 if else while intなど
- 識別子 要するに変数とかに使える名前 hoge fuga foo bar
※空白文字やコメントは無視
- 字句解析
- ソースコードを一文字すつ読んでトークンに切り分ける。
- トークンの種類も調べる。
字句解析 (続き)
要するにこんな感じ
実際には一気にソースコードをすべてトークン列に変換するのではなく、構文解析をしながら少しずつトークンに変換していく
研究報告書の字句解析の節の後半で書いているのは一気にソースコードをトークン列に変換しようとすると詰むという話
構文解析
トークンの配列から構文木を作る
人力でやる場合、再帰下降型パーサでやることが多い(たぶん)
字句解析とか構文解析とかめんどい人へ
世の中には便利なツールがありまして・・・
- lex,flex
- 字句解析コードを自動生成
- yacc,bison
- 構文解析コードを自動生成
実際に処理系を作るならこういうツールを使うのもありかも
実行コード生成
- 構文木から命令列に変換
- 識別子(変数名とか関数名とか)の解決
- ジャンプ先アドレスの解決
VM設計
- スタックマシン
- ローカル変数はコールスタックフレームに直接置かないで
ローカル変数を抱えているスコープオブジェクトへの参照を持つ - 変数へのアクセスはスコープオブジェクトの連鎖をたどって行う
- コールスタックフレームはthisで参照されるオブジェクトを持つ
スコープオブジェクト?
- 各スコープに対応するスコープオブジェクトが存在する
- それぞれのスコープオブジェクトは
それぞれのスコープに属するローカル変数を保有している - 親スコープへの参照を持っている
どうしてこんなものが必要?
クロージャの実装のために必要!!
クロージャとは
関数の中で関数を定義できる、かつ
内側の関数から外側の関数のスコープにアクセスできる機能
クロージャとは (続き)
何が問題?
関数の実行を抜けた後もスコープが生きている必要がある
↓
コールスタックフレームにローカル変数を置けない!!
関数呼び出し(C言語の場合)
関数呼び出しを抜ければその関数のローカル変数は用済み。
→コールスタックフレームにローカル変数を直接置いておけばよい。
関数呼び出し(JavaScriptの場合)
関数呼び出しを抜けたあともローカル変数が参照されるかも。
→コールスタックフレームにローカル変数を直接置けない。
→スコープへの参照をコールスタックフレームに持たせる。
関数呼び出し(JavaScriptの場合 続き)
関数を呼ぶときはその関数のもつスコープオブジェクトを親とする新しいスコープオブジェクトを生成
スコープオブジェクトの処分はガベージコレクションに任せる。
今後の予定
- for in文の実装
- 例外処理の実装
- 組み込みオブジェクトの実装
その他今後の希望
バインドを楽にしたい、VMコード生成時の最適化など