csvのデータをSATySFiで使う方法

LuaLaTeXでcsvファイルを処理するにはLuaの力を借りれば簡単にできることは常識だと思うので、これを[SATySFi](https://github.com/gfngfn/SATySFi)でもやってみました。


# やりたいこと

```textcolor.csv
red,hoge
blue,fuga
black,
red,hoge
blue,fuga
,hogehoge
red,hoge
blue,fuga
black,hoge
blue,fuga
```

というcsvファイルに対し、「`red,hoge`から色が`red`で`hoge`という文字列を出力する」という操作を行います。
全部揃って整っている形だとつまらないので一部抜けがあっても対応できるようにします。
つまり、結果はこうなるはずです。

<font color="Red">hoge</font><font color="Blue">fuga</font><font color="Red">hoge</font><font color="Blue">fuga</font><font color="Red">hoge</font><font color="Blue">fuga</font><font color="Black">hoge</font><font color="Blue">fuga</font>


# 方針

## 参考：LuaLaTeXでの場合

TeXLiveをインストールすれば標準でtexluaがインストールされますし、LuaLaTeXならばそのtexluaを利用してtexファイルの中からluaファイルを実行することが可能です。

なので、内部でluaファイルを呼び出し「一行読んで\textcolor{}{}に適切な値を入れるという作業をファイルが終わるまで繰り返す」という動作を実行させるだけで終わります。

簡単ですね。

## SATySFiでの場合

SATySFiではファイル読み込みとプログラミングができるため、CSVファイルの中身を解析してデータを取り出し、それを元に組版することが自前でできます。

[satyrographos](https://github.com/na4zagin3/satyrographos)というSATySFiのパッケージマネージャを使う前提で進めます。

SATySFiでは既に[CSVパーサ](https://satyrographos-packages.netlify.app/packages/csv)と、CSSで事前に定義されている色名から色情報を生成する関数が[baseパッケージ](https://github.com/nyuichi/satysfi-base/blob/master/src/color-ext.satyg)の一つとして公開されています。

そのため、この2つを組み合わせることで簡単に実装することができます。


## SATySFiでの場合（旧）

SATySFiでは昔はファイル読み込み機構がありませんでした、そのため、READMEに従えばインストールされているはずのOCamlを利用してがんばります。

まずはOCamlでﾌﾟﾖｸﾞﾔﾐﾝｸﾞをしてcsvファイルからsatyhファイルを作ります。そのsatyhファイルの中身はこんな感じの`(color * inline-text) list`となる`csvlist`関数を定義しているものです。

```csv.satyh
let csvlist = [
(red, {hoge});
(blue, {fuga});
(red, {hoge});
(blue, {fuga});
(red, {hoge});
(blue, {fuga});
(black, {hoge});
(blue, {fuga});

]

```

次に、この`csvlist`に対して、`color * inline-text`から「色が`color`で中身が`inline-text`の文字列を出力する」という関数を適用させて`inline-boxes list`にします。そしてこれらを`++`を使って結合させることで`csvlist`を`inline-boxes`型にして`+p{}`コマンドに渡します。

作成されたものは以下で確認できます。

- [GitHubのリポジトリ](https://github.com/puripuri2100/SATySFi-csv-test)

# 実装

satyrographosを用いて必要な2つのパッケージをインストールします。

```
opam install saytsfi-base satysfi-csv
satyrographos install
```

その後、次のような文書ファイルを書きます。

```
% 文書クラス
@require: stdjabook
% 色情報生成関数を使う
@require: base/color-ext
% リストの処理のための関数を読み込む
@require: base/list-ext
% 文字列取り扱いのための関数を読み込む
@require: base/string
% CSVパーサ
@require: csv/csv


let-rec textcolor ctx csv =
  match csv with
  | [] -> inline-nil
  | x::xs -> (
    let color =
      match List.nth 0 x with
      | Some(s) -> if String.is-empty s then Color.black else Color.of-css s
      | None -> Color.black
    in
    let text =
      match List.nth 1 x with
      | Some(s) -> embed-string s
      | None -> {}
    in
    let ib = read-inline (set-text-color color ctx) text in
    ib ++ textcolor ctx xs
  )


let-inline ctx \textcolor filename =
  let csvtext =
    filename
    |> read-file
    |> List.mapi (fun i s -> (
      if i == 0 then s else (string-unexplode [0x0A])^s
    ))
    |> String.concat
  in
  let csv-data = CSV.parser csvtext in
  match csv-data with
  | Ok(csv) -> textcolor ctx csv
  | Err(_) -> inline-nil

in

document (|
  title = {CSVファイル処理};
  author = {};
  show-title = true;
  show-toc = false;
|) '<
  +p{\textcolor(`textcolor.csv`);}
>
```

あとはCSVデータをtextcolor.csvというファイル名で保存をしてから

```
satysfi doc.saty
```

などでコンパイルをすると、求めていたような色とテキストの列になっていると思います。

# まとめ

OCamlの力を借りないでもSATySFiの力だけで、csvファイルの中身を処理することができるようになりました。便利ですね。