Skip to content

マクロでコード中の位置を取得・使用する機能

Takashi Suwa edited this page Dec 15, 2020 · 3 revisions

概要

2020年1月25日の0.0.4のリリースで導入された多段階計算とマクロの機構により,より簡潔な記述と素早いエラー発見を両立したDSLをSATySFi上で定義して使える状況が或る程度整いました.しかし,このマクロ機構ではエラーの際はどんなエラーになったかの種類を標準出力に出して停止することはできても具体的にどこが悪かったかを(型エラーのように)コード中の位置を指し示して報告することはできませんでした.そこで,マクロを適用して処理するステージ0の文字列の内容は実際にソースファイル中のどこにあるのかをエラー時に指し示せるようにするために,コード中の位置がステージ0で取得できる文字列リテラル @`…` を導入しました(commit hash: 03d3991).

API

まず,input-position という新しい基本型が導入されました.そして位置つきの文字列リテラル @`…` がステージ0で input-position * string 型をつけて扱われます.このリテラルを書くと,文字列の内容の先頭位置とそこから始まる実際の内容の組の値として扱われます.input-position 型の値からは以下のような型をもつ新しいプリミティヴ get-input-position を用いることでファイル名・行番号・何文字目かを表す組を得ることができ,これを使ってコード中の位置を明示したエラー報告なりなんなりができるという算段です.

val get-input-position : input-position -> string * int * int

簡単な例

勿論エラー報告に限らず正常系でも使えます.最もシンプルな例として,単にメッセージと共に書いた行が取得できるマクロを示します:

@stage: 1

let-inline \message-with-pos@ ~poss =
  ~(let (ipos, msg) = poss in
    let (fname, ln, _) = get-input-position ipos in
    &(let it-msg = embed-string ~(lift-string msg) in
      let it-fname = embed-string ~(lift-string fname) in
      let it-ln = embed-string (arabic ~(lift-int ln)) in
      {#it-msg; (at line #it-ln; in #it-fname;)}
    )
  )

possinput-position * string 型の引数であり,ここからファイル名 fnameln を取り出しています.

こうして定義したマクロ \message-with-pos@ を以下の要領で使うと,

14| document (|
15|   title = {Position};
16|   author = {gfn};
17| |) '<
18|   +p{
19|     \message-with-pos@~(@`Hello, macro!`);
20|   }
21| >

以下のような結果が得られます:

スクリーンショット 2020-12-15 0 22 26

やったぜ.

今後の発展

エラー報告に使うために言語そのものに追加する機能としてはこれで十分ですが,簡単にエラー報告できるようにするには位置をトラックするパーサーコンビネータなどを整備する必要があると思います(satysfi-base ライブラリparser パッケージを拡張するとよさそうです).また,SATySFiは当初あまり文字列を加工してほしくないという動機から文字列操作の機能が意図的にかなり貧弱になっていましたが,大々的にDSLを使えるようにするには文字列加工の言語機能やライブラリを整備する必要がありそうです(これも一応既に satysfi-basestring パッケージがわりと強力ではあります).

また,細かいこととして,今のまだリリースしていない実装ではファイル名としてbasenameだけが返るようになっていますが,絶対パスを返すようにした方が良さそうです.

Clone this wiki locally