Node.jsの抽象構文木を操作してソースコードを生成する
「ソースコード生成してみたい!けど、そういや1年くらい前にASTって言葉を知ってから何もしてなかったな」、と思ってASTを書いてそいつを食わせてソースを生成してみた。
もともとはさいきょうのスタブサーバーを作りたいねって話から始まったからそういう感じのリポジトリ名の以下リポジトリに実際に今夜書いたコードが乗っかってる。
構文木ってなに?
プログラムの構造を、(Javascriptの場合)JSON形式にパースしたもの。 何段階かに分かれているっぽい。
- もとのソースコード: ソースコードそのまま。
- トークン:
['let', 'hoge', '=', 'fuga', '(', '1', ')']
みたいな、実際に書いてある文字列を適切な箇所で分割したリスト。 - 木構造: それをもとにもとのソースコードを生成できるような連想配列。
詳しくは以下のブログエントリーで。詳しい記事ありがとうございます。
どうやってASTを生成するの?
ASTの仕様を知ってそれを満たすJSONを構築すれば、それをASTからソースに変換するライブラリに食わせればコード生成できる。 ソースコード ⇔ ASTのライブラリはJS界にいくつかあるみたいで、今回はEsprimaを使うことにした。 Babelもトランスパイラー?だから同じような機構を持ってるみたいだけど、特に理由なくEsprimaにした。
JavascriptのASTの構造はestreeという標準があるらしい。
けど、それを読むより実際にソースコードから生成されるASTを見たほうが早いなってことで以下で試す。
http://esprima.org/demo/parse.html
こいつに
let express = require('express');
みたいなのを食わせると
{ "type": "Program", "body": [ { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": { "type": "Identifier", "name": "express" }, "init": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "require" }, "arguments": [ { "type": "Literal", "value": "express", "raw": "'express'" } ] } } ], "kind": "let" } ], "sourceType": "script" }
みたいな木構造にバラしてくれる。 逆にこいつを、対応したコード生成機に食わせればソースコードが生成されるってわけよ。
これを使えば、カッコとかカンマとかシングルクォートとか改行とか気にせずに、オブジェクトをガツガツ組み立てていけば思うままのソースを生成できる。
これを世に出してGithubのStarを稼ぐんだ、という野望を胸に書くコードは、心なしかいつもよりギラギラして見えた。