Skip to content

SATySFiのテキスト出力モードのプロトタイプ

puripuri2100 edited this page Jan 19, 2019 · 4 revisions

テキスト出力モードとは

SATySFiの テキスト出力モード (単に テキストモード とも)は,簡単に言えばPDFだけでなくHTMLやLaTeXなどのテキストファイルを共通の文書ソースから出力できるようにすることを意図したモードです.SATySFiは通常

$ satysfi 〈f_in:入力文書ファイル名〉 -o 〈f_out:出力先PDFファイル名〉

という形で起動されることにより文書ファイル(およびそこから @import:@require: でリンクされている拡張子が .satyh または .satyg のパッケージたち)を読み込み,それをもとにPDFを生成します.この処理に使われる各種パッケージでは版面上にどのように文字やグラフィックスを配置するかが様々なインラインコマンドやブロックコマンドに対応する定義として与えられているわけですが,一方で文書ファイル本体は(組版処理をどのように行なうかに関する情報はすべてパッケージへと切り離して)文書の “内容” と “構造” のみを記載することができます.よく言われる “構造と体裁の分離” が仕組み上それなりに実現しやすいわけです(“構造と体裁の分離” という考え方自体の妥当性はここでは扱わないことにします).

ここで,次のようなアイディアが浮かびます:

文書ファイル本体に “内容” と “構造” のみが記述されており,したがって “実際にどのような組版処理を施すか” の情報が含まれていないなら,PDF出力に使っていた各パッケージを(PDF出力時と同等のコマンドを定義した)テキスト出力用のパッケージへそれぞれ取り替えることで,同一の文書ファイルからHTMLやLaTeXなどのテキストファイルが生成できてもよさそうではないか?

テキスト出力モードはこの発想を実現しようとするものです.大昔(=2017年6月以前)のことをご存知の方なら,SATySFiの前身となるソフトウェアである Macrodown が実現していた機能の再来だと感じられるかもしれません.

テキスト出力モードの起動方法

以下のように文字列引数を伴う --text-mode オプションをつけて起動するとSATySFiがテキスト出力モードで動作します.

$ satysfi --text-mode 〈ss:形式指定文字列〉 〈f_in:入力文書ファイル名〉 -o 〈f_out:出力先テキストファイル名〉

〈ss〉 はコンマ区切りで形式名称を与えるもので,例えば "html" とか "jsarticle,platex,latex,tex" のような具合の文字列です.仕組みは後述しますが, “より限定的な形式” ほど手前に書きます.なお,通常のPDF出力モードのときと違い,出力ファイル名の指定 -o … は省略できません(PDF出力モードのときは出力ファイル名を省略すると入力文書ファイル名のbasenameに .pdf をつけた名前が自動で補われますが,テキスト出力モードでは拡張子を自動で決定する方法を今のところ提供していないので,コマンドラインで必ず与える必要があります).

テキスト出力モードでのパッケージの読み込み

テキストモードでの @require: 〈foo:パッケージ名〉 の処理方法は少し特殊です.上で与えた形式指定文字列 〈ss〉 をコンマで切り分けた各文字列を 〈s_1〉〈s_2〉, … , 〈s_N〉 とおくと,@require: 〈foo〉 に対してSATySFiは

  • 〈LIBROOT〉/dist/packages/〈foo〉.satyh-〈s_1〉
  • 〈LIBROOT〉/dist/packages/〈foo〉.satyh-〈s_2〉
  • 〈LIBROOT〉/dist/packages/〈foo〉.satyh-〈s_N〉
  • 〈LIBROOT〉/dist/packages/〈foo〉.satyg

をこの優先度で探し,最初に見つかったものをパッケージ 〈foo〉 の実装として読み込みます(ただし 〈LIBROOT〉ライブラリルート のパスとします.The SATySFi​book 第7章参照).例えば形式指定文字列が "jsarticle,platex,latex,tex" だった場合, @require: theorem に対する実装は

  • 〈LIBROOT〉/dist/packages/theorem.satyh-jsarticle
  • 〈LIBROOT〉/dist/packages/theorem.satyh-platex
  • 〈LIBROOT〉/dist/packages/theorem.satyh-latex
  • 〈LIBROOT〉/dist/packages/theorem.satyh-tex
  • 〈LIBROOT〉/dist/packages/theorem.satyg

の優先度で探索されます.裏を返せば,テキスト出力モードに対応するパッケージを提供する側は,この仕組みに基づいて各形式 〈s_i〉 ごとにパッケージの実装を与えることになります.また,上の規則からもわかるように,.satyg はPDF出力モードとテキスト出力モードとで共通に使える実装に対して与える拡張子です.例えば List モジュールを提供する list パッケージは組版処理にもテキスト処理にも依存せずいずれにも使うことができるため,実装は list.satyg というファイル名のパッケージファイルで与えられています.

テキスト出力モード用プリミティヴ

テキスト出力モードでは,PDF出力モードでは使えたような組版処理に関する型やプリミティヴの多くが使えないようになっています.代わりに,PDF出力モードでは使えなかった以下のような型とプリミティヴが提供されます:

  • text-info 型: テキスト処理情報 の型.文書内容を文字列に変換するために必要な情報を蓄えている.PDF出力モードの context 型に対応するもの.
  • get-initial-text-info : unit -> text-info: 最初のテキスト処理情報を受け取る.PDF出力モードの get-initial-context に対応するもの.
  • break : text-info -> string: テキスト処理情報に基づいて改行と(適切な量の)インデントを挿入する.
  • deepen-indent : int -> text-info -> text-infodeepen-indent 〈i〉 〈tinfo〉〈tinfo〉 のインデント量を 〈i〉 だけ増やしたテキスト処理情報を返す.
  • stringify-inline : text-info -> inline-text -> string: インラインテキストを出力文字列に落とす.PDF出力モードの read-inline に相当する.
  • stringify-block : text-info -> block-text -> string: ブロックテキストを出力文字列に落とす.PDF出力モードの read-block に相当する.

テキスト出力モード用のコマンドの定義

インラインコマンドに対する定義は,PDF出力モードの場合とほぼ同様に次のような構文で定義します:

let-inline 〈y:テキスト処理情報引数の変数名〉 〈\cmd:コマンド名〉 〈通常の引数のパターンの列〉 = 〈expr:式〉

PDF出力モードとの違いは以下のとおりです:

  • let-inline 直後に置く “第0変数” 〈y〉 には(context 型の代わりに) text-info 型がつけられる
  • 〈expr〉 は(inline-boxes 型ではなく)string 型がつくことが要請される

実装例

html-base パッケージ

HTMLの出力に使える html-base パッケージ(実装は dist/packages/html-base.satyh-html)は次のような函数を提供しています:

  • HTMLBase.tag : (string * string) list?-> bool?-> int?-> text-info -> string -> (text-info -> string) -> stringHTMLBase.tag ?:〈props〉 ?:〈br〉 ?:〈indent〉 〈tinfo〉 〈tag_name〉 〈k〉 の形で使う.各引数に与えるべき内容は以下のとおり:
    • 〈props〉: プロパティ名とその値の対応をリストで与える.省略した場合は [] と扱う.
    • 〈br〉: 開始タグの直後と終了タグの直前で改行するか否か.省略した場合は true,つまり改行する.
    • 〈indent〉: 開始タグと終了タグの間で改行した場合のインデント量の増加.省略した場合は 2
    • 〈tinfo〉: テキスト処理情報.
    • 〈k〉: 適切に更新されたテキスト処理情報が渡されてタグの内側の内容を返す継続.

用例

次のような文書 example.saty をHTMLに変換したいとしましょう:

@require: small-class

document (|
  title = {Example};
|) '<
  +p{
    My quiz above the kiwi juice
    needs priceless fixing.
  }
>

勿論,この文書ファイルからPDFを出力するには document+p を定義した small-class.satyh をつくって

$ satysfi example.saty

を叩けばよいですが,今回はこの同一の文書ファイルからHTMLを生成することを考えているわけです.すなわち,

$ satysfi --text-mode "html" example.saty -o example.html

を実行してテキスト出力モードで起動したときにうまくHTMLにしてくれるような small-class.satyh-html を定義します.その実装例が以下です:

@require: list
@require: option
@require: html-base

let concat = List.fold-left (^) ` `

let document r bt =
  let tinfo = get-initial-text-info () in
    concat [
      `<!DOCTYPE html>` ^ (break tinfo);
      HTMLBase.tag tinfo `html` (fun tinfo ->
        concat [
          HTMLBase.tag tinfo `head` (fun tinfo ->
            HTMLBase.tag ?* ?:false tinfo `title` (fun tinfo ->
              stringify-inline tinfo r#title
            )
          );
          HTMLBase.tag tinfo `body` (fun tinfo ->
            stringify-block tinfo bt
          );
        ]
      );
    ]

let-block tinfo +p it =
  HTMLBase.tag tinfo `p` (fun tinfo -> stringify-inline tinfo it)

これを用いて出力されるのが以下のようなHTMLです:

<!DOCTYPE html>
<html>
  <head>
    <title>Example</title>
  </head><body>
    <p>
      My quiz above the kiwi juice
      needs priceless fixing.
    </p>
  </body>
</html>

このような要領でHTMLへの変換を実装できます.ここでは出力結果も可読性が高くなるようにインデント量に気をつかう実装にしましたが,出力結果のインデントを気にしない場合はテキスト処理情報を持ち回す必要がないので,もっと簡素な実装になるでしょう.

Future Work

数式のテキストへの変換が重要なのですが,これを扱うための仕組みがまだ設計できていません.数式は出力結果を用いる環境によって使える表現方法が限られているため,MathMLや(グリフのアウトラインを落とし込んだ)SVGなど,変換方法を定義する或る程度一般性の高い機能が必要で,そのための筋の良い設計がまだ練り切れていないのです.特にアウトラインを落とし込むSVGを出力したい場合はテキスト出力モードでもPDF出力モードのような組版処理機能が使えるようになっていることが前提となり,これが言語やシステム全体の設計を複雑化させる要因となっています.

まとめ

現状のプロトタイプでは数式を含まない文書に対してしか動作しませんが,SATySFi文書はテキスト出力モードを使用することで(適切なパッケージを与えれば)HTMLやLaTeXといった他のテキスト形式へと変換することができます.

Clone this wiki locally