Next: , Previous: , Up: プログラムソースの解析   [Contents][Index]


37.6 複数言語ののパース

プログラミング言語のソースの一部に他の言語のソースが含まれているときがあります。一例としてはHTML + CSS + JavaScriptが挙げられます。このような場合には、別の言語によって記述されたテキストセグメントには別のパーサーを割り当てる必要があります。伝統的にこれはナローイングの使用によって達成されてきました。tree-sitterはナローイング(narrowingを参照)とともに機能しますが、推奨される方法はバッファーテキストのリージョン(範囲)にそれを操作するパーサーを指定する方法です。このセクションではパーサーにたいして範囲のセットや取得を行う関数について説明します。

Lispプログラムがバッファーでパーサーを使う前には、treesit-update-rangesの呼び出しによってパーサーそれぞれにたいする範囲が正しいか確認して、その位置にあるテキストにたいして任を負うパーサーを解決する必要があります。この2つの関数自身は作業を行わず、実際に作業を行うにはメジャーモードがtreesit-language-at-point-functionおよびtreesit-language-at-point-functionをセットする必要があります。これらの関数および変数については、このセクションの終わり近くで詳細に説明しましょう。

範囲の取得とセット

Function: treesit-parser-set-included-ranges parser ranges

この関数はrangesにたいして処理を行なうためにparserをセットアップする。parserが読み込むのは指定された範囲のテキストのみ。ranges内の範囲はそれぞれ(beg . end)という形式のペアーである。

rangesの範囲は、以下の疑似コードのように重複せず順番に並んでいなければならない。

(cl-loop for idx from 1 to (1- (length ranges))
         for prev = (nth (1- idx) ranges)
         for next = (nth idx ranges)
         should (<= (car prev) (cdr prev)
                    (car next) (cdr next)))

rangesがこの制約に違反したり、何か他の問題が発生した場合には、この関数はtreesit-range-invalidエラーをシグナルする。シグナルデータには特定のエラーメッセージ、セットを試みた範囲が含まれている。

この関数は範囲を無効にするためにも使うことができる。rangesnilの場合には、パーサーはバッファー全体をパースするようにセットされる。

例:

(treesit-parser-set-included-ranges
 parser '((1 . 9) (16 . 24) (24 . 25)))
Function: treesit-parser-included-ranges parser

この関数はparserにセットされている範囲をリターンする。リターン値はtreesit-parser-included-rangesranges引数と同じく(beg . end)という形式のコンスセルのリスト。parserが範囲を何ももっていなければリターン値はnil

(treesit-parser-included-ranges parser)
    ⇒ ((1 . 9) (16 . 24) (24 . 25))
Function: treesit-query-range source query &optional beg end

この関数はsourcequeryでマッチングしてキャプチャーされたノードをリターンする。リターン値は(beg . end)という形式のコンスセルのリスト。ここでbegendはそれぞれテキスト範囲の開始と終了をする。

利便性のためにsourceは言語シンボル、パーサー、あるいはノードでもよい。この関数はそれが言語シンボルならその言語を使用する最初のパーサーのルートノード、パーサーならそのパーサーのルートノード、ノードならそのノードでマッチを行なう。

引数queryはノードのキャプチャーに用いるクエリー(tree-sitterノードにたいするパターンマッチングを参照)。引数begendがどちらも非nilなら、それはこの関数がクエリーを行なう範囲を制限する。

他のクエリー関数と同じように、この関数はqueryが不正であればtreesit-query-errorエラーをraiseする。

Lispプログラムで複数言語をサポートするには

一般的なLispプログラムにおいて言語が複数ミックスされたプログラムをサポートするには、以下の2つの関数を呼び出すだけで十分です。

Function: treesit-update-ranges &optional beg end

この関数はバッファーのパーサーの範囲を更新する。この関数はパーサーの範囲がbegendの間に正しくセットされているかをtreesit-range-settingsに照らして確認する。省略された場合のデフォルトはbegがバッファー先頭、endがバッファー終端となる。

たとえばフォント表示(fontification)を行なう関数は、リージョン内のノードにクエリーを行う前にこの関数を使用する。

Function: treesit-language-at pos

この関数はバッファー位置posにあるテキストの言語をリターンする。その背後ではtreesit-language-at-point-functionを呼び出して、そのリターンされた値をリターンしている。treesit-language-at-point-functionnilの場合には、この関数はtreesit-parser-listのリターン値から最初のパーサーの言語をリターンする。バッファーにパーサーがなければnilをリターンする。

メジャーモードで複数の言語をサポートするには

ミックスされているかもしれない一連の言語では、ホスト言語(host language)と1つ以上の埋め込み言語(embedded languages)が存在することが珍しくありません。Lispプログラムはまずホスト言語のパーサーでドキュメント全体をパースすることで情報を得てから、それを用いて埋め込み言語の範囲をセット、その後に埋め込み言語をパースするのです。

HTMLCSS、それにJavaScriptを含むバッファーを例にとります。LispプログラムはまずHTMLパーサーでバッファー全体をパースして、それからパーサーにCSSとJavaScriptに相当するstyle_elementscript_elementのノードをクエリーするのです。その後にCSSとJavaScriptそれぞれにたいして、対応するノードが跨がる範囲をセットします。

シンプルなHTMLドキュメントが与えられると:

<html>
  <script>1 + 2</script>
  <style>body { color: "blue"; }</style>
</html>

LispプログラムはまずHTMLパーサーでパースを行い、それからCSSとJavaScriptそれぞれのパーサーにたいして範囲をセットします:

;; パーサーの作成
(setq html (treesit-parser-create 'html))
(setq css (treesit-parser-create 'css))
(setq js (treesit-parser-create 'javascript))

;; CSSの範囲をセット
(setq css-range
      (treesit-query-range
       'html
       '((style_element (raw_text) @capture))))
(treesit-parser-set-included-ranges css css-range)

;; JavaScriptの範囲をセット
(setq js-range
      (treesit-query-range
       'html
       '((script_element (raw_text) @capture))))
(treesit-parser-set-included-ranges js js-range)

treesit-update-rangesによってEmacsがこのプロセスを自動化します。treesit-update-rangesがプロセスを自動化する方法を解決するためには、複数言語のメジャーモードがtreesit-range-settingsをセットする必要があります。treesit-range-settingsに割り当てられる値を生成するためには、メジャーモードがヘルパー関数treesit-range-rulesを使う必要があります。この操作を直接コード化したのが以下のセッティング例になります。

(setq treesit-range-settings
      (treesit-range-rules
       :embed 'javascript
       :host 'html
       '((script_element (raw_text) @capture))

       :embed 'css
       :host 'html
       '((style_element (raw_text) @capture))))
Function: treesit-range-rules &rest query-specs

この関数はtreesit-range-settingsをセットするために用いる。クエリーのコンパイルやその他の後処理に注意を払い、treesit-range-settingsにセットできるような値を出力する。

この関数は引数として一連のquery-specを受け取る。ここでquery-specとは0個以上のkeyword/valueペアーが前置されたqueryのこと。queryはそれぞれ文字列、S式、コンパイル済みフォーム、あるいは関数のいずれかによるtree-sitterクエリーである。

queryがtree-sitterクエリーなら:keyword/valueのペアーを2つを前置すること(:keyword:embedは埋め込み言語、:hostはホスト言語)。

treesit-update-rangesは埋め込み言語用のパーサーにたいして範囲をセットする方法の解決にqueryを使用する。ホスト言語パーサーにqueryを問い合わせてキャプチャーされたノードが跨ぐ範囲を計算、それらの範囲を埋め込み言語パーサーに適用するのである。

queryが関数の場合にはkeywordvalueのペアーは必要ない。関数の場合にはstartendという2つの引数を受け取り、カレントバッファーでstartendの間にあるリージョンでパーサー用の範囲をセットすること。その関数がstartendの間のリージョンを包むような広いリージョンに範囲をセットしても問題はない。

Variable: treesit-range-settings

これはバッファーでtreesit-update-rangesがパーサーにたいする範囲を更新する際の助けとなる変数である。settingのリストであり、その正確なフォーマットは内部的な使用を意図している。この変数が保持できる値を生成するにはtreesit-range-rulesを使うこと。

Variable: treesit-language-at-point-function

この変数の値はバッファー位置posを単一の引数として受け取り、posにあるテキストの言語をリターンする関数であること。この変数はtreesit-language-atにより使用される。


Next: tree-sitterを用いるメジャーモードの開発, Previous: tree-sitterノードにたいするパターンマッチング, Up: プログラムソースの解析   [Contents][Index]