UglifyJSのパーサを試してみる
前回の記事の続き。
asyncblockのソースを調べたところ、asyncblockのsource transformationは、内部でUglifyJSを使っていた。
UglifyJSは普通はソースをminifyする用途で使われるが、その一部としてjsの文をparseしており(当然か)、その部分だけ抜き出して使うこともできる。
ということで今回は、var foo = 12 + 34 * 56; という単純な文をパースしてみて、どのようになるか試してみる。
準備
npm install uglify-js
スクリプト
var parse = require('uglify-js').parse; var code = 'var foo = 12 + 34 * 56;' var result = parse(code); // ネストが深かったので、今回はresult.bodyだけを出力。(配列だったけど中身1つだけだったので、body[0]を出力した) console.log(JSON.stringify(result.body[0]));
結果(整形後)
結果を整形したところ、ものすごい長かったです。
何行目の何文字目等、直接文法に関係しないところを全部とっぱらった結果が以下
{ // var "start": { "type": "keyword", "value": "var" }, // ; "end": { "type": "punc", "value": ";" }, // startが"var"の時は、その中身は"definitions"になるということだろう。 // 代入文 "definitions": [ // definitionsの中身はname(変数名)とvalue(代入する値) // foo: 12 * 34 + 56 { "start": { "type": "name", "value": "foo" }, "end": { "type": "num", "value": 56 }, // 代入される変数名 foo "name": { "start": { "type": "name", "value": "foo" } "end": { "type": "name", "value": "foo" }, "name": "foo", }, // 代入する値。値といってもまだ子がついてる部分木 // 12 * 34 + 56 "value": { "start": { "type": "num", "value": 12 } "end": { "type": "num", "value": 56 }, // + "operator": "+", // +演算子の左がわ。12 "left": { // 最も小さい単位のトークンは、start・end・(value / name など?)が全て同じように定義される模様 "start": { "type": "num", "value": 12 }, "end": { "type": "num", "value": 12 }, "value": 12 }, // +演算子の右側。34 * 56 "right": { // ここも最も小さいトークン。それ以降はなし "start": { "type": "num", "value": 34 }, "end": { "type": "num", "value": 56 }, "operator": "*", // *演算子の左側。34 "left": { "start": { "type": "num", "value": 34 }, "end": { "type": "num", "value": 34 }, "value": 34 }, // *演算子の右側。56 "right": { "end": { "type": "num", "value": 56 }, "start": { "type": "num", "value": 56 }, "value": 56 }, }, } } ], }
まとめ
構文解析木内のどのノードにも、start, end, 中身を表すキーがある。(葉ノードの時とかはstart, end, value全て同じようなデータになってるみたい。)
中身を表すキーは、
- var宣言文… definitions
- 変数への代入… name(変数名), value(代入する中身。今回は数式)
- 数式… oprator(+とか*), left(演算子の左)、right(演算子の右)
- 数値などの値… value
次はこれをどう操作しているのかを見ていこうと思います。…が、asyncblockはuglify-jsを自分でカスタマイズしたのを使っているようなので、その辺ちゃんと解読できるか心配。