こんにちは。フロントエンド開発課のkoki_matsuraです。
今回はX(旧Twitter)で話題になっていたポスト(旧ツイート)
を見て、実際にGritのチュートリアルを通して触ってみたので、備忘録的な感じで軽くGritの概要やクエリ言語であるGritQLの基礎的な構文を紹介させていただきます。
目次は以下のようになっています。
Gritとは
ソフトウェアのメンテナンスを簡潔にしてくれるツールです。
具体的には対象のコードを目的のコードベースの状態に変換してくれるものです。
Gritの大部分は静的解析によるコード操作を可能にするクエリ言語であるGritQLと変換を目的のコードベースのルールに適用させるAIにより構成されています。
現在はベータバージョンです。
GritQLとは
GritQL言語とはコードの検索と変換のためのGritのクエリー言語です。
少ない行数のクエリで複雑な変換を可能としています。
チュートリアル
ここからは実際にGritQLをチュートリアルを通して触っていきたいと思います。いろいろなクエリが書かれています。Patternsの最初の例を見てみましょう。
下の画像にある「Run Pattern」を押すとその例のクエリが書かれたエディタが右に出てきます。
エディタの上がGritQLで下がインプットのコードです。
また、もう少し大きいエディタで書きたい場合は右上のリンクからStudioを使いましょう。
初期画面にはデフォルトで何かのクエリが書いてあるかもしれませんが、消せば問題なく自由に書くことができます。
インプットのコードも自由に編集可能です。
コードの検索
最初はconsole.log("Hello world!")
の例を見てみましょう。
この例はシンプルでインプットのコードの中からconsole.log("Hello world!")
を検索しています。
例えば、console.log(42)
に書き換えてみましょう。すると、インプットコードの反応する部分が変わります。
非常に簡単に検索ができます。しかしながら、一つ注目するべきなのはコードには反応していますが、コメントには反応しないことです。これはGritQLが単純な文字列マッチングでなく、JavaScriptとして機能しているものに反応するということです。
なので、以下のようなコメントではない文字列の場合もJSではないので反応しません。
`Hello world!`
しかし、単純に文字列を検索したい時もあると思います。そのような時は、""
で囲むことで任意の文字列検索をできます。
メタ変数
メタ変数とは特に意味のない変数のことです。特に日本ではhoge
やfuga
が有名だと思います。
先ほどのconsole.log("Hello world!")
の例ではコンソールの中身が「Hello world!」でないと反応しません。これでは実用性は低いです。
そこでメタ変数を使いましょう。使い方は以下のようにします。
console.log($message)
これでコード上のconsole.log
をキャッチできます。
GritQLでメタ変数を使うためにはマッチさせたい部分を好きな変数名にして先頭に$
マークをつけるだけです。
コードの変換
検索もですが、変換をできるところがGritQLの目玉機能となっています。
やり方はシンプルで、変換したい値 => 変換後の値
です。
以下の例ではJavaScriptのvar
をconst
に置き換えるものです。
var
を文字列として検索しています。
"var" => `const`
結果は以下のようにvarをconstに書き換えられています。
条件付き変換
変換に対して条件を設けるのも簡単です。SQLと同じでwhere句
を使います。
例えば、実用性は低めですが、特定のconsoleだけをalertにしたい場合は以下のようにします。
`console.log($message)` => `alert($message)` where { $message <: `"これをalertにする"` }
<:
はマッチ演算子といい、$message
が"これをalertにする"
と一致するかを見ています。
結果は以下のように「これをalertにする」以外のconsoleは書き換えられていません。
GritQLではwhere
だけでなく、or
やand
なども使えます。
例えば、以下のクエリは公式が出している「Non-strict == => strict ===」です。orとwhereを使った実用的なものです。
or { `$x == $y` => `$x === $y`, `$x != $y` => `$x !== $y` } where { $y <: not `null` }
この例では等価・不等価演算子を厳密等価・不等価演算子に変換をしています。条件として、比較の右側がnull
ではない時のみになります。
結果は以下のように$y == null
以外は厳密等価・不等価演算子に変換されています。
パターン修飾子
パターン修飾子というのは先ほど使ったand
やor
などのマッチング方法を変換するもののことを言います。
単体で使うのではなく、複数を組み合わせることでより柔軟に対応することができます。
公式の「Function expressions to arrow functions」を例に取ります。
これは名前からもわかると思いますが、関数の書き方を変換するものです。
or { `function ($args) { $body }` => `($args) => { $body }` where { $body <: not contains { or { `this`, `arguments` } } until `function $_($_) { $_ }` }, `($args) => { return $value }` => `($args) => $value` }
今までの例と比べるとかなり複雑に見えますが、少しずつ解釈していけば難しくありません。
1行目のor
はコードからfunction ($args) { $body }
か($args) => { return $value}
のどちらかに当てはまるものを抽出してきています。
($args) => { return $value}
に関しては以下のように($args) => $value
に変換しているだけです。
function ($args) { $body }
の方は($args) => { $body}
に変換するのですが条件をつけています。
この条件の中でcontains
とuntil
が新しく出てきます。それぞれ説明します。
contains: 特定のパターンを含んでいるかどうかをチェックします。今回の場合は
$body
の中にthis
またはarguments
を含んでいないことが条件となっています。until:
contains
と共に用いるもので、パターンチェックをどこまでするのかを決められます。今回の場合では、function $_($_) {$_}
にマッチする構文にぶつかるまでパターンチェックを行うように設定しています。
ちなみに、$_
は匿名メタ変数といい、ワイルドカード的な感じで使うものです。
$body <: not contains { or { `this`, `arguments` } } until `function $_($_) { $_ }`
よって、上記のパターンは関数の処理の中でfunction $_($_) { $_ }
にぶつかるまでにthis
またはarguments
を含んでいないことを条件に持ちます。
結果は以下のようになります。
rememberはfunction $_($_) { $_ }
がそもそもないので、関数の処理内容全体がcontainsの対象となり、thisを含んでいるため変換されていません。
sumToValueはthisがありますが、function $_($_) { $_ }
の中にあり、containsの対象にはならないため変換されています。
パターンの再利用
GritQLのクエリも他のプログラミング言語の関数同様に再利用したいことがあります。
そのような時は、パターンとして定義しましょう。書き方はpattern ${パターン名} (${引数}) { ${クエリ} }
のような書き方をします。
例えば、console.logを消すパターン「delete_console_log」は以下のように作成できます。
pattern delete_console_log () { `console.log($message)` => . }
作成したパターンは関数実行と同様にdelete_console_log()
で使えます。
console.logのみが消されていることも以下から確認できます。
一部省いたものもありますが、以上がチュートリアルで紹介されているものでした。
終わりに
今回はGritQLを触ってみました。
クエリでコードを一気に変換できるのはとても魅力的ですよね。まだベータバージョンなのですが、できることが非常に多くて楽しいです。
機会があれば、応用編としてGritQLで実務にも使えそうなカスタムパターンを作って、記事を書きたいと思うので、期待していただけると幸いです。
また、チュートリアルでは紹介しきれなかったパターンや条件演算子などもあるので、公式のドキュメントを読むことをお勧めします。
最後まで読んでいただきありがとうございました!