AST explorerへ独自にパーサーを導入する手順

AST explorerで未対応のパーサーを独自に追加して使えるようにする方法を紹介します。

AST explorerとは

AST explorerとは、AST(abstract syntax tree・抽象構文木)をツリー表示で確認できるサイトです。

AST explorerの例

Go、Python、JavaScript、HTMLなど主要な言語のパーサーに対応しています。

左側のエディタで入力したものを右側にツリーまたはJSON形式で表示します。ツリーの各プロパティは折りたたみできます

カーソル位置のノードがハイライトされるため、パーサーが返す値の構造を理解しやすくなります。
ASTを使って何かを作りたいときには必須級のツールです。

未対応のパーサーを追加する手順

ここからが本題です。AST explorerで未対応のパーサーを追加する手順を紹介します。

※ 追加できるパーサーはNode.jsで動くものだけです。

リポジトリの準備

まずはリポジトリfkling/astexplorerをクローンかフォークしましょう。

リポジトリ名コピー用
[email protected]:fkling/astexplorer.git

ディレクトリの移動

websiteというディレクトリの中で作業します。

Terminal window
cd website/

Node.jsのバージョン切り替え

このリポジトリはNode.jsの16にしか対応していません。というわけで、Node.jsのバージョンを切り替えられるツールを使って切り替えましょう。

筆者はmiseを使い、このリポジトリのみNode.jsの16で動くようにしました。

Terminal window

サーバーの立ち上げ

パッケージをインストール後、ビルドしてサーバーを立ち上げます。browserslistの更新もしてくれと警告が出たため、その更新も実行しました。

Terminal window
npx update-browserslist-db@latest
yarn install
yarn run build
yarn run start

ビルドは結構長いです。

ブラウザでhttp://localhost:8080/を開いてローカルで動かせることを確認できたら準備完了です。

Built wi React, Babel, ...のみ表示され、エディタやツリーが表示されない場合は後述の「トラブルシューティング」の項目を参考にしてください

パーサーの追加

いよいよパーサーの追加です。この記事では、Bladeのパーサーstillat-blade-parserを例にします。

パーサーのインストール

まずはパーサーをインストールします。

Terminal window

ファイルの追加

src/parsers/の下に、追加したいパーサーの言語名やフレームワーク名でディレクトリを作っておきます。

Terminal window
mkdir src/parsers/blade

続いて、ファイルを3つ作成します。index.js言語名-parser.jsは自分で作るよりも他のパーサーのファイルをコピーしたほうが速い&楽です。

  • index.js
  • codeExample.txt
  • 言語名-parser.js

ひとつずつ見ていきましょう。

index.js

index.jsには、表示名やどんな拡張子として扱うかを書きます。

例として追加するBladeの実体はphpであるため、src/parser/php/index.jsを参考にしました。

index.js
import 'codemirror/mode/php/php';
export const id = 'blade';
export const displayName = 'Blade';
export const mimeTypes = ['application/php'];
export const fileExtension = 'php';

1行目のCodeMirrorは画面左側に表示されるエディタで使われるもののようです。CodeMirror: Language Modesに対応している言語の一覧があります。対応言語がない場合、importしなくOKです。

codeExample.txt

codeExample.txtには、例として表示するコードを書きます。
使いたいパーサーに合わせて書いてください。

<p>{{ $target }}</p>
<style>
p {
padding: 0;
}
</style>

言語名-parser.js

言語名-parser.jsには、パーサーの結果をAST explorerが受け取る処理を書きます。

blade-parser.js
import defaultParserInterface from '../utils/defaultParserInterface';
import pkg from 'stillat-blade-parser/package.json';
import { BladeDocument } from 'stillat-blade-parser/out/document/bladeDocument';
const ID = 'blade-parser';
const defaultOptions = {};
export default {
...defaultParserInterface,
id: ID,
displayName: ID,
version: pkg.version,
homepage: pkg.homepage,
locationProps: new Set(['startPosition', 'endPosition']),
typeProps: new Set(['constructor.name']),
_ignoredProperties: new Set([
'refId',
'parentIndex',
'parentTypeIndex',
'parent',
'originalAbstractNode',
'parser',
]),
loadParser(callback) {
callback(new BladeDocument());
},
parse(bladeDocument, code) {
bladeDocument.getParser();
bladeDocument.loadString(code);
return bladeDocument.getRenderNodes();
},
getNodeName(node) {
return node.constructor.name;
},
nodeToRange(node) {
if (node.startPosition && node.endPosition) {
return [node.startPosition.offset, node.endPosition.offset];
}
},
};

ハイライトしたところがメインとなるロジックです。loadParserで読み込んだパーサーのオブジェクトをparseで受け取ってノードを返します。

他の部分はツリー表示に関する設定です。

たとえばlocationPropsには、各ノードのどのプロパティが位置を示すのかを書いておきます。
getNodeNameには、ノード名としてどのプロパティを使うかを書きます。bladeのパーサーではノードのプロパティとしてノード名を持たないようだったため、そのクラスオブジェクトのクラス名を返すようにしました。

詳しい仕様はdefaultParserInterfaceの定義をたどって見てみましょう。コメントアウトにたくさん書いてあります。

次の画像が実際にstillat-blade-parserを動かしてみた例です。

stillat-blade-parserの例

トラブルシューティング

筆者が遭遇したトラブルとその解決方法も書いておきます。

ブラウザで表示されない

Built wi React, Babel, ...のみ表示され、エディタやツリーは表示されないケースがあります。

ビルドではなくyarn run watchを実行したうえでデベロッパーツールのconsoleを確認すると、次のように表示されていました。

Warning: Failed prop type: The prop `parser` is marked as required in `SettingsDialog`, but its value is `undefined`.
Uncaught TypeError: Cannot read properties of undefined (reading 'category')

筆者のケースではキャッシュが原因でした。
ブログの検証のためにfkling/astexplorerのリポジトリを複数用意しており、別のリポジトリでは追加していないパーサーがキャッシュにあったためです。

次の手順でキャッシュを削除しましょう。

  1. デベロッパーツールを開く
  2. Applicationタブを開く
  3. Local storage > localhostのURL > explorerSettingsV1を右クリック
  4. Deleteを選択して削除
cacheの削除

フリーズする

エディタでコードを書き換えようとするとフリーズするケースがあります。パーサーが返すノードの中に、パーサー自体を保持するようなプロパティなどが存在するとフリーズします。

_ignoredPropertiesにそのプロパティを書いて非表示に変更しましょう。

_ignoredProperties: new Set([
'parser', // フリーズするノードのプロパティ
]),

以上、AST explorerをローカルで動かす時のパーサーの追加方法でした。意外と簡単に動かすことができて感動です。

GitHubのリポジトリeetann/astexplorerに今回動かしたコードを公開しています。参考にしたい方はどうぞ。