-
Notifications
You must be signed in to change notification settings - Fork 84
追加したい機能とそのイメージ
将来的に追加したいと思っている機能を具体的なコード例のイメージと共にこのページにひとまず書き留めておきます.
パッケージ中にいわゆるdoc commentを書けるようにする.
基本的にはシグネチャの type
,val
などの宣言の直後にその説明を書く:
module List :> sig
#[doc {
`List.map @f@ @xs@` でリスト `@xs@` の各要素に
函数 `@f@` を適用してできる各値を元の順序のまま並べたリストを返す.
}]
val map : ('a -> 'b) -> 'a list -> 'b list
...
end = struct
...
end
ドキュメントを組むのに用いるために #[doc-require ...]
によって別のパッケージを読み込める:
% bnf.satyh
require Math
#[doc-require Code]
#[doc-require Math]
#[doc-require Bnf] % ドキュメント生成時には自身を通常のパッケージとして読み込む
module Bnf :> sig
#[doc '<
+p{
BNFによる構文定義を組むコマンド.
第1引数が定義されるメタ変数,第2引数の各要素が並列して組まれる各行となる.
}
+p{
例として,以下のように記述すると,
\d-code(```
\Bnf(${M})[
${| c | x | M\ M | \lambda x.\ M |};
${| \text!{let}\ x = M\ \text!{in}\ M |};
];
```);
以下のように組まれる:
\Bnf(${M})[
${| c | x | M\ M | \lambda x.\ M |};
${| \text!{let}\ x = M\ \text!{in}\ M |};
];
}
>]
val \Bnf : inline [math; list (list math)]
...
end = struct
...
end
以下を実行することでパッケージ 〈Pkg〉
のドキュメントを組む:
$ satysfi gendoc 〈Cls: ドキュメント生成用クラスファイル〉 〈Pkg: ドキュメントを生成したいパッケージ〉
クラスファイル 〈Cls〉
はドキュメント生成のために以下のようなコマンド等を提供する必要がある:
-
+val-entry : block [string, list (string * kind-representation), type-representation; option block-text]
-
val
に対するdoc commentを組む. - doc commentのない公開されている函数については第4引数に
None
が渡される. -
type-representation
は型の構文を表現するデータ型.
-
-
+type-entry : [string, list (string * kind-representation), option type-representation, option block-text] block-cmd
-
type
に対するdoc commentを組む. - 第1引数は型パラメータの列.
- 第2引数は,opaque typeの場合は
None
,transparent typeの場合はSome(_)
が渡される. - doc commentのない公開されている函数については第3引数に
None
が渡される,
-
-
+p : block [inline-text]
- 通常の段落.
document : block-text -> document
-
ドキュメント生成の際に読み込むパッケージの指定
#[doc-require 〈パッケージ名〉]
- 読み込むパッケージは自身でもよい.
-
値に対するdoc comment
#[doc {〈It: X について説明する文のインラインテキスト〉}] val 〈X: 名前〉 〈Q: 型変数の量化〉 : 〈T: 型〉
-
+val-entry(`〈X〉`)(〈Q を表現するデータ〉)(〈T を表現するデータ〉)(Some '<+p{〈It〉}>);
というコードに変換される.
-
-
値に対するdoc comment(複数段落にしたい場合)
#[doc '<〈Bt: X について説明する文のブロックテキスト〉>] val 〈X: 名前〉 : 〈T: 型〉
-
+val-entry(`〈X〉`)(〈Q を表現するデータ〉)(〈T を表現するデータ〉)(Some '<〈Bt〉>);
と書いたものして扱われる.
-
-
型に対するdoc comment
#[doc {〈It〉}] type 〈X: 名前〉 〈Params: 型パラメータの列〉 = 〈T: 型〉
-
+type-entry(〈Params を表現するデータ〉)(`〈X〉`)(Some '<+p{〈It〉}>);
と書いたものとして扱われる. -
#[doc '<〈Bt〉>
]` の場合も同様.
-
-
一般の段落
#[doc-p '<〈Bt〉>]
-
〈Bt〉
を直接書いたものとして扱われる.
-
数式をテキスト出力モードに対応させたいが,PDF出力向けに整備された既存の仕組みをそのまま使い回すのではかなりぎこちない.そのぎこちない方法も含めて何通りかの実現方法の候補があるので,以下に掲げておく.かなり雑然とした説明になってしまうが,いつまでも何も書かないよりは遥かに有意義なので書く,という程度の記述であることを了承されたい.
まず,通常の数式である math
型は以下に示すような定義をもつ組み込みの型であり,${ … }
などによる記法は math
型の式を書くための糖衣構文であるとする:
type math-class =
| MathOrd
| MathBin
| …
type math =
math-element list
type math-element =
| MathSuperscript of math * math
| MathSubscript of math * math
| MathFraction of math * math
| MathRadical of math
| MathUpperLimit of math * math
| MathLowerLimit of math * math
| MathGroup of math-class * math-class * math
| MathChar of math-class * math-char
| MathParen of math-paren * math-paren * math
| MathParenWithMiddle of math-paren * math-paren * math-paren * math list
| MathGeneralInline of inline-result
math-class
は 数式クラス という(スペーシング等に関連する)数式アトムの種別を列挙した型.
ひとまず math-element
型のコンストラクタのうち手前の7種類をみる.MathSuperscript(m1, m2)
は m1
に対して指数 m2
をつけてつくる数式のデータである.同様に MathSubscript(m1, m2)
,MathFraction(m1, m2)
,MathRadical(m)
,MathUpperLimit(m1, m2)
,MathLowerLimit(m1, m2)
はそれぞれ添字,分数,根号,上積み,下積みの構造によって構成される数式のデータである.MathGroup(mclass1, mclass2, m)
は数式 m
を(内的に決まる数式クラスに従わずに)左側に対して数式クラス mclass1
,右側に対して数式クラス mclass2
でスペーシング上扱うものと指定する.
最後の4つがPDFとテキストの共通化に関して重要である.MathChar(mclass, mchar)
は数式記号 mchar
を数式クラス mclass
として組む数式の指定である.また MathParen(paren1, paren2, m)
は数式 m
を左の括弧 paren1
と右の括弧 paren2
で囲って出力する数式の指定であり,MathParenWithMiddle(paren1, paren2, paren, ms)
は paren
を “セパレータ” として数式のリスト ms : math list
の各要素を 左の括弧 paren1
と右の括弧 paren2
の間に並べて組む指定である.最後のものはわかりづらいが,集合の内包表記や量子力学によく登場するブラケット記法など,左右の括弧に高さを合わせて伸びるグリフが括弧内にもある場合に使う.MathGeneralInline(inline)
は “通常のテキストの処理結果” inline
を数式中に埋め込む.
そして,これらの引数には以下のような型が使われる:
-
math-char
型: 数式中の(ローマン,イタリックといった数式文字クラスに依存しない)記号を表す型である.すぐ後で述べるが,PDF出力モードとテキスト出力モードで定義が変わりうる.PDF出力の場合は,添字や指数がついたときのカーニングの入れ方の指定なども含む. -
math-paren
型: 括弧のグリフ等を指定するための型.これも後述するがPDF出力モードとテキスト出力モードで定義が変わりうる. -
inline-result
型: これは単純で,PDF出力モードではinline-boxes
,テキスト出力モードではstring
.
ところで「通常のラテン文字やギリシア文字のように “ローマンとかイタリックといった指定に依存して変化する文字” はどうやって扱うのだろう?」と疑問に思う必要はない.これらは “よりフロントエンド側の部分” で吸収すればよい問題だからである.これについては 1.3 で後述する.
さて,math-char
型の “実体” はどう定めればよいだろうか? 既に述べたように,これはPDFモードとテキストモードで別々に提供すべき部分である.というのも “PDF出力に向けては ⊆
に,LaTeXコードの出力に向けては \subseteq
に,MathMLの出力に向けてはやはり ⊆
にする” といった文字単独に起因する差異をここに集約するためである.
まずテキスト出力モードの場合は基本的には単に string
型にするだけでよい.これにより,例えば \subseteq
コマンドは(出力も単に \subseteq␣
という文字列にしたいので)以下のようにLaTeX出力向けに定義できる:
let-math tinfo \subseteq =
MathChar(MathRel, `\subseteq `#)
ここで let-math
によるコマンド定義は(let-inline
や let-block
と同様に)外から文脈が渡されてくるようになっているものとする.
しかし,実際には文字色など “構造から自然に決まるわけではない追加の体裁情報” も出力に含められるように定式化したいので,このような追加の体裁情報を文脈 tinfo
から取り出して埋め込んでおく必要がある.そこで以下のように math-char
を定める:
type display-spec = (|
color : color;
…
|)
type math-char = (|
main : string;
spec : display-spec;
|)
要するに spec
というフィールドに文字色など追加の体裁情報をとれるようにしたもの.これに基づくと,上に掲げた \subseteq
の定義は以下のように変わる:
let-math tinfo \subseteq =
let spec = get-math-char-spec tinfo in
MathChar(MathRel, (|
main = `\subseteq `#;
spec = spec;
|))
ただし get-math-char-spec : text-info -> display-spec
は別の箇所で定義されていて,文脈から必要な体裁情報を抽出する補助函数であるとする.
PDFモードの場合も,文字色などの体裁情報を扱う必要はあるものの基本的には単に string
型の延長にすれば事足りる.実際,現行のv0.0.4のSATySFiでは
val math-char : math-class -> string -> math
というプリミティヴが提供されており,構築方法自体はここで考えた MathChar
とほぼ等価になっている.
ただし,PDFモードでは(別のテキスト形式への変換ではなく自前で組版処理を行なう場合に特有な指定として)カーニング方法の指定が入ることがある.実際,現行のSATySFi v0.0.4では以下のようなプリミティヴが提供されている(これはv0.0.1当初からある):
type math-kern = length -> length -> length
val math-char-with-kern : math-class -> string -> math-kern -> math-kern -> math
math-kern
は組み込みの型名ではないがここで便宜的に使っている.この第3・第4引数は “この文字アトムに添字や指数がついたときに高さに応じてどれだけカーニングを施すべきか” を指定した函数である.これらも扱うためには,math-char
は以下のような型へと拡張されねばならない:
type display-spec = (|
kern : (math-kern * math-kern) option;
color : color;
…
|)
type math-char = (|
main : string;
spec : display-spec;
|)
そして従来の math-char
と math-char-with-kern
に相当する函数を以下のように定義する:
let math-char : context -> math-class -> string -> math
| ctx mcls s =
let me =
MathChar(mcls, (|
main = s;
spec = (| kern = None; color = get-text-color ctx; … |);
|))
in
[me]
let math-char-with-kern : context -> math-class -> string -> math-kern -> math-kern -> math
| ctx mcls s kernL kernR =
let me =
MathChar(mcls, (|
main = s;
spec = (| kern = Some(kernL, kernR); color = get-text-color ctx; … |);
|))
in
[me]
この新しい math-char
を用いると,\subseteq
は次のような定義になる:
let-math ctx \subseteq =
math-char ctx MathRel `⊆`
(要加筆)
PDFを出力する場合は基本的には math-element
型を用いて構築される数式のデータをあらためて分解したりする必要はなく,(現行のSATySFi v0.0.4の embed-math : context -> math -> inline-boxes
のような)math
型のデータを渡されたらそれを組む数式エンジン的な機構がプリミティヴとして備わっていればよい.
一方でテキストを出力する場合は一度組み上げた数式を文字列に落とし込む処理が必要である.\math
コマンドの実装は以下のような具合になる:
let make-math-class-command mcls =
match mcls with
| MathOrd -> `\mathord`
| MathBin -> `\mathbin`
| MathRel -> `\mathrel`
…
let make-color-spec color =
match color with
| RGB(r, g, b) -> `[rgb]{` ^ show-float (r /. 255.) ^ `,` show-float (g /. 255.) ^ `,` ^ show-float (b /. 255.) ^ `}`
…
let-rec output-math-element tinfo me =
match me with
| MathSuperscript(m1, m2) -> output-math tinfo m1 ^ `_{` ^ output-math tinfo m2 ^ `}`
| MathSubscript(m1, m2) -> output-math tinfo m1 ^ `^{` ^ output-math tinfo m2 ^ `}`
| MathFraction(m1, m2) -> `\frac{` ^ output-math tinfo m1 ^ `}{` ^ output-math tinfo m2 ^ `}`
…
| MathChar(mcls, mchar) ->
let clscmd = make-math-class-command mcls in
let str = mchar#main in
let color-spec = make-color-spec mchar#spec#color in
clscmd ^ `{\textcolor` ^ color-spec ^ `{` ^ str ^ `}}`
and output-math tinfo m =
m |> List.map (output-math-element tinfo) |> List.fold-left (^) ` `
let-inline tinfo \math m =
`\(` ^ output-math tinfo m ^ `\)`
前述のように,ラテン文字やギリシア文字のような “ローマンとかイタリックといった指定に依存して変化する文字” は math
型よりもフロントエンド側での処理により扱える.例えば \Gamma
は次のように扱える:
let-math ctx \Gamma =
let str =
match get-math-class ctx with
| MathItalic -> `𝛤` % U+1D6E4
| MathBoldItalic -> `𝜞` % U+1D71E
| MathRoman -> `Γ` % U+0393
| MathBoldRoman -> `𝚪` % U+1D6AA
| _ -> `Γ` % U+0393 (dummy)
in
let me =
MathChar(MathOrd, (|
main = str;
spec = (| color = get-text-color ctx; kern = None; … |);
|))
in
[me]
ラテン文字はこれに相当する定義を文脈に適切なプリミティヴで突っ込んでおき,数式を走査した際にそのときの文脈に基づいて変換する仕組みにするとよい.
(要加筆)
- トップページ
- The SATySFibook Web公開版 第1版
- Wiki
- 目的別パッケージ一覧
- コマンドライン書式
- SATySFiコマンド一覧
- Satyrographos(パッケージマネージャ)
- 新しい言語機能の紹介
- 言語機能の構想