以下の内容はhttps://zrbabbler.hatenablog.com/entry/2025/04/19/193254より取得しました。


Typstで\expandafterする方法(展開制御編)

前回は「Typstで \expandafter する」を実現するために「絵を描く」という方針を採用しましたが、これには「TeX言語🤮的要素が乏しい」という問題点がありました。TeX言語的なネタに仕上げようとするなら、やはりトークン列の展開制御」にマジメに取り組む必要がありそうです。Typstで「TeXトークン列の展開制御」を行うには一体どうすればいいのでしょうか。

Typstのパッケージで \expandafter しようと試みる

Typstはトッテモスゴイので「Typst上でTeXのコードを実行する」ためのパッケージが既に作られています。Universeで軽く探してみたところ、以下のものが見つかりました。

MiTeXで \expandafter したかった話

「TypstでTeXのコードを実行する」ためのパッケージの中で恐らく一番有力そうなのがMiTeXです。Rustで実装されたWASMプラグインでの処理のため高速に動作するのが特徴です。例えば以下のような機能があります。

  • mi(«LaTeX記述»)[content]: LaTeXのインライン数式のコードをTypstのインライン数式に変換する。
  • mitex(«LaTeX記述»)[content]: LaTeXのディスプレイ数式のコードをTypstのブロック数式に変換する。

«LaTeX記述»の引数は「文字列またはrawエレメント1」で与える規則になっていて、これは以降で紹介する他のパッケージについても同様です。\エスケープが不要なrawエレメントが便利なので以降の例ではこちらを使います。

例えば以下のようにしてTypst文書にLaTeXの数式を書けます。

#import "@preview/mitex:0.2.5": *

Result: // `…`の中にTeXコードを書く
#mi(`c = \sqrt{a^2 + b^2}`)

出力結果

ちゃんとTeXのコードを扱えているようです。ではさっそく \expandafter を使ったコードを試してみましょう!

#import "@preview/mitex:0.2.5": *

Result:
// \expandafter 的なコードを書いてみる
#mi(```
\def\Duck{d_uc^k}
\def\Twice#1{#1#1}
\expandafter\Twice\Duck
```)

\expandafter\Twice\Duckを一回展開すると\Twice d_uc^kとなり、さらに\Twiceを展開して結果はdd_uc^kとなるはずです。

このソースをコンパイルすると……うまくいきません😢

error: plugin errored with: error: unknown command: \Duck
   ┌─ @preview/mitex:0.2.5\mitex.typ:18:8

しかも「\Duckが未定義である」という不可解なエラーになっています。少し調べてみた感じでは、MiTeXはTeX言語🤮レベルの処理をサポートしていないようです2。つまり\sqrt3等のLaTeXの命令は全て一種のプリミティブとして“直接実行”される仕組であると思われます。

結局、そもそも「トークンの展開制御」という概念がないため「\expandafter できる」ことは当然全く期待できません(ざんねん🙃)

m-jaxonで \expandafter したかった話

m-jaxonパッケージは「WASMプラグインJavaScript処理系(QuickJS)を動作させて、その上でMathJaxを動かす」という(やや強引💪な)方法でLaTeXの入力を処理します。同じ作者がJavaScript処理系を動かすためのjogsというパッケージを作製していて、このm-jaxonパッケージはその応用例として作られたようです。

※元々実用目的ではないためメンテナンスされておらず、Typstの0.12.0版以前でしか動作しません4。以下では0.10.0版での動作結果を示します。

m-jaxonパッケージでは以下の機能が使えます。

  • render(«LaTeX記述»)[content]: LaTeXの数式をMathJaxでレンダリングした組版結果のコンテンツ。
    オプション引数:
    • inline[bool;既定=false]: インライン数式であるか(偽ならブロック数式)。

MathJaxの利用者なら知っているかもしれませんが、MathJaxではTeX言語🤮レベルの機能(\def\over等)がある程度サポートされています。だから次のようなコードが動作します。

// Typst v0.10.0を使用
#import "@preview/m-jaxon:0.1.1"

Result:
 // 一旦 \expandafrer を取りやめ
#m-jaxon.render(```
\def\Duck{d_uc^k}
\def\Twice#1{#1#1}
\Twice\Duck
```, inline: true)

\expandafterのない\Twice\Duckは一回展開すると\Duck\Duckとなり、最終的な展開結果はd_uc^kd_uc^kとなります。

出力結果
想定通りの結果なので期待がもてますね。\expandafterを付けてみましょう!

// Typst v0.10.0を使用
#import "@preview/m-jaxon:0.1.1"

Result:
// \expandafter を復活
#m-jaxon.render(```
\def\Duck{d_uc^k}
\def\Twice#1{#1#1}
\expandafter\Twice\Duck
```, inline: true)

出力結果(失敗😢)

おっと、赤文字が出てしまいました😲 MathJax使用者にはお馴染みだと思いますが、赤文字は当該箇所でエラーが発生したことを表します。この場合のエラーは要するに「\expandafter定義されていない」ということです。m-jaxonを使っても「Typstで \expandafter する」のは無理なようです(ざんねん🙃)

それでも \expandafter しようと頑張ってみる

絶望的な状況😭になってしまいました。既存のTypstパッケージは \expandafter をサポートしていません。しかもその原因は「WASMで動いているTeXコード処理器が \expandafter をサポートしていない(恐らくTeXの展開処理を模倣していない)」からであるため、Typst側のコードを改修して解決する見込みもありません。もはや「新しいWASMプラグインをつくる」くらいしか道は残されてないのでしょうか……😟

……いや待ってください。m-jaxonパッケージはJavaScriptTeXコード処理器であるMathJaxを使っていましたが、著名な「JavaScriptTeXコード処理器」はもう1つあります。そう、KaTeXです。

KaTeXで \expandafter したい話

KaTeXは「MathJaxの代替となる」ことを目指して開発されたJavaScriptライブラリで、高速に動作することを特徴としています。そして、今回の件において重要なことですが、\expandafter をサポートしています。これは実際にKaTeXのサイトトップにあるデモアプリで試してみれば判ります。

デモアプリで \expandafter する様子

※実はKaTeXはTeXトークン列の展開をある程度模倣する動作を行います。なので \expandafter だけでなく \noexpand や \futurelet も使えます😲

KaTeXもMathJaxと同じくJavaScriptのライブラリなので、m-jaxonが「Typst上でMathJaxを動かしている」のと同じ手順を踏めば「Typst上でKaTeXを動かす」ことができるはずです。

ただし、MathJaxの代わりにKaTeXを使う場合には注意点があります。それは「KaTeXから受け取ったデータを数式として組版する簡単な手段がない」ことです。MathJaxのAPIには「SVGとして出力する」機能があり、m-jaxonはSVGを経由5して実際に数式を版面に出力しています。ところがKaTeXのサーバサイドAPIには「MathMLまたはHTMLとして出力する」機能しかないので、組版した数式を出力できないのです。

従って、KaTeXを使う方針には実用的価値はほとんどありません。とはいえ、今回の話の目的はあくまで「\expandafter を動かすこと🤯」であり、数式とかには全くサッパリ興味はないので、この点は全く問題になりません。

KaTeXで \expandafter してみる話

というわけで、早速つくってみました💪

このk-tieモジュール6を使うには以下の手順を行います。

k-tieモジュールは以下の機能をもちます。

  • to-mathml(«LaTeX記述», …)[str]: LaTeXコードをKaTeXを用いてMathMLコードに変換する。«LaTeX記述»は複数指定可能(可変長)で、改行区切りで連結した文字列がKaTeXに渡される。
  • to-string(«LaTeX記述», …)[str]: to-mathmlでMathMLに変換した後に、結果のMathMLデータに含まれる文字だけを抽出した文字列を返す。\expandafter の動作確認用🙃

それではまずk-tieが想定通りに動作するかを確認してみましょう。

#import "k-tie.typ"

#k-tie.to-string(```
\alpha\times\beta
```)

週力結果

どうやらうまくいっているようです🙂

今の場合、to-string()がMathMLから拾い出した文字列は「α×β」となり、これがそのまま版面に出力されています。数式として出力しているのではないことに注意してください。フォントについてもテキスト用の“Libertinus Serif”が使われています。

「Typst上でKaTeXを動かす」ことに成功したので、早速 \expandafter が正しく動作するかを試してみましょう!

#import "k-tie.typ"
                    
// 前提のマクロ定義
#let macros = ```
\def\Duck{d_uc^k}
\def\Twice#1{#1#1}
```

// \expandafter なし
- #k-tie.to-string(macros, `\Twice\Duck`)
// \expandafter あり
- #k-tie.to-string(macros, `\expandafter\Twice\Duck`)

出力結果

イカンジですね8😊

KaTeXでもっともっと \expandafter してみる話

最後に「Typst上で \expandafter を“ちゃんと”処理できるか」を確認してみましょう。もちろんこれは「KaTeXが \expandafter を“ちゃんと”処理できるか」に依存しています。先ほどよりも複雑な、「\expandafter 鎖」を含んだコードを実行してみます。

#import "k-tie.typ"
                    
// 前提のマクロ定義
#let macros = ```
\def\A{\B\B}\def\B{\C\C}\def\C{88}
\def\Enclosed#1{[#1]}
```

// 展開制御しない(\expandafter なし)
- #k-tie.to-string(macros, ```
\Enclosed\A
```)
// 先に \A を1回展開(\expandafter 1個)
- #k-tie.to-string(macros, ```
\expandafter\Enclosed\A
```)
// 先に \A を2回展開(\expandafter 3個)
- #k-tie.to-string(macros, ```
\expandafter\expandafter\expandafter\Enclosed\A
```)
// 先に \A を3回展開(\expandafter 7個)
- #k-tie.to-string(macros, ```
\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter\Enclosed\A
```)

出力結果

カンペキ😍

まとめ

というわけで、春のTeX言語キャンペーン🌸絶賛開催中です!😃


  1. rawエレメント自体はcontent値であって文字列(str値)ではありません。
  2. このため\defという機能もないので\defは単に無視されて、次の\Duckを実行しようとして(もちろんTeXの文法に従えばこんなことはありえない)未定義エラーになります。ちなみに、\expandafterも単に無視されます。
  3. もちろんLaTeXでは\sqrtTeXのマクロとして実装されています。
  4. 0.10.0版以前の古いintrospectionの書き方を使っているので、0.12.0版では非推奨警告となり、0.13.0版ではエラーになります。
  5. Typstのimage()関数はSVG画像をサポートしています。
  6. typst.tomlを用意してあるので、ローカルレポジトリにインストールしてパッケージとして使うこともできます。
  7. 「Assets」の下にある「tarball」および「zip」のリンクからアーカイブをダウンロードできます。
  8. 数式中の上付・下付の添字については、to-stringで文字列に変換する際にその情報が落ちてしまいますが、もちろんMathMLのコードの中では反映されています(興味がある人はto-mathmlで確認してみてください)。



以上の内容はhttps://zrbabbler.hatenablog.com/entry/2025/04/19/193254より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14