
目次
はじめに ▼ DSSSLとは? ▼ DSSSL標準の部分 ▼ スタイル言語 ▼ フローオブジェクト ▼ 変形言語 ▼ 文書モデル ▼ 質問言語 ▼ DSSSLプロセッサーの入手 ▼ Jade ▼ Seng ▼ Emacs ▼ スタイルシートの作成 ▼ 組立規則 ▼ 非常に単純な例 ▼ 定義と式 ▼ 定義 ▼ 式 ▼ 入れ子の式 ▼ sosofo ▼ 子供に対する個別の処理 ▼ IDによる要素の処理 ▼ モード ▼ より複雑な文書型 ▼ フローオブジェクト ▼ プログラミング機能 ▼ ブール ▼ 条件式 ▼ 手続き ▼ 今後どう学ぶべきか ▼
Paul Prescod氏が著した“Introduction to DSSSL”[
http://itrc.uwaterloo.ca/~papresco/dsssl/tutorial.html]の全訳を掲載します。この訳は1997年7月24日版に基づきます。訳の文責は岸 和孝にあります。
DSSSL標準のJIS規格書がまだ刊行されておりませんので,用語は関連資料を元にして適当に訳しました。参考のため初出の原語は〔〕内に示しました。訳の不明な箇所がありましたら,ご指摘下さい。
DSSSLについて紹介します。私は,皆さんに次のことをお願いします。先ず,誤植があれば知らせて下さい。次に,分かりにくい箇所があれば教えて下さい。何回も読まなければならないような難しい箇所は直したいと思います。最後に,ナイジェリア国での人権と環境犯罪についてシェル石油をボイコットして下さい。それを理解するための時間をとって,友人や家族にそうするように話して下さい。また,私は,この問題[
http://www.wadham.ox.ac.uk/~rhouston/action/shell_leaflet.html]についてどんな人とも論議したいと思っています。
さて,DSSSLは,Document Style Semantics and Specification Language(文書スタイル意味指定言語)であり,SGML(Standard Generalized Markup Language)と一緒に使うことを意味しています。この頭字語は何とも理解しにくいと心配されるかもしれませんが,恐れることはありません。SGMLについて分からないのであれば,私が書いた“SGML Introduction”[
http://itrc.uwaterloo.ca/~engl210e/BookShelf/Tutorials/SGML/sgmlint.htm]か“Gentle Introduction to SGML”[
http://sable.ox.ac.uk/ota/teip3sg]を読んで下さい。
DSSSLは,SGML文書〔SGML document〕に関わる結合処理〔associating processing〕の国際標準です。ご存知の通り,SGMLそれ自身は,文書の内容(文,構造,リンク)を(通常は)書式付け〔formatting〕に結び付けられた処理から完全に分離できるはずです。Word for Windows,TeX,LaTeXの文書は,その文書がどのように見えるか(言い換えれば,プリンターはそれをどのように処理すべきか)を記述しますが,SGML文書は構造についてだけ記述します。DSSSLを用いて,標準の方法で文書の処理を記述できます。文書処理における最もありふれた形態は書式付け〔formatting〕と変形〔transformation〕ですので,DSSSLでは最初にこれらの処理を規格化しました。それらが必要なように,さらに他のものが加わるかもしれません。最初の二つの処理は,非常に強力であり,SGML文書処理の世界をDSSSLが「変形する」と思われます。
DSSSLは,多くの異なる部分に分けられます。たとえ標準の部分が現在のニーズに応じられないとしても,残りの部分はできるかもしれないことを意味します。
スタイル言語〔style language〕は,SGML文書の書式付けを記述するための標準化された強力な言語を備えています。ここで述べるDSSSLプロセッサーがスタイル言語を実行します。あなたはウェブからそれらをダウンロードし,(文書型定義を伴った)SGML文書とスタイルシート〔stylesheet〕を作り,DSSSLプロセッサーを実行してみて下さい。それは,ワードプロセッサーや植字〔typesetting〕フォーマットが何であれ,書式付けした文書〔formatted document〕を作るか,または直ぐに書式付けした結果を表示します。ここでは,スタイル言語だけを扱います。「変形」とは,一つの文書型定義に適合する文書を別の文書型定義から作ること,または,一つの文書型定義の文書の構造を変えることです。
フローオブジェクト〔flow object〕は,文書のレイアウト〔layout〕を記述するために標準の機構〔mechanism〕に備えています。それらは,ページ列〔page sequence〕,段落〔paragraph〕,ハイパーリンク〔hyperlinks〕,図版〔artwork〕のようなレイアウト構成体〔layout construct〕を表わします。フローオブジェクトは,ページ列のマージン,段落のフォントサイズ,ハイパーリンクの参照先〔destination〕,画像の高さと幅といったような特性〔characteristic〕を持ちます。
普通は,スタイル指定〔style specification〕からフローオブジェクトを作ります。しかし,スタイル言語を用いていないシステム(例えば,PerlやC++のようなプログラミング言語で駆動する書式付け処理,または完全なDSSSL表現言語〔DSSSL expression language〕をサポートしていないワードプロセッサーやブラウザー)では,同じレイアウトモデル(フローオブジェクトの同じ集合)を用いることが道理にかなうかもしれません。SGMLを用いていない環境でも,出版ツール〔publishing tool〕が共通のレイアウトモデル〔layout model〕を共有すれば,一つのツールから別のツールへの情報の移転は,今後はより容易な処理になるかもしれません。
変形言語〔transformation language〕は,一つの文書型定義についてマーク付けされたSGML文書を別の文書型に変形するための標準の言語です。例えば,TEI文書をDocBookやHTMLへ変形できます。変形言語は,表現言語〔expression language〕と呼ばれる大きな共通の部分集合を持っていますので,スタイル言語に似ています。
DSSSLは,最近修正されたHyTime標準の文書モデル〔document model〕を共有します。このモデルは,グローブ〔grove:「森」の意味〕の中に編成されたノード〔node:「節」の意味〕としての文書か文書集合〔set of documents〕を記述します。たぶん,SGML文書の中の要素〔element〕が,大きくはブランチ〔branch:「枝」の意味〕としての親要素〔containing element〕,小さくはリーフ〔leaves:「葉」の意味〕としての要素でツリー〔tree:「木」の意味〕を形作っているかを想像できるでしょう。DSSSL/HyTimeグローブモデル〔grove model〕は,この概念を拡張します。グローブはツリーのツリーです。属性値〔attribute value〕のようなものは,主要な要素ツリー〔element tree〕に取り付けられたツリーで表現されます。文書型定義は,SGML文書ツリー〔SGML document tree〕に取り付けてあるツリーです。ブランチまたはツリーのリーフは,ノードと呼ばれます。このすべてを実際に理解できるかどうかを心配することはありません。DSSSLの利用は,それに依存しないからです。グローブについて興味があれば,ウェブ上の資料[
http://www.sil.org/sgml/topics.html#groves]で学ぶことができます。
DSSSLプロセッサーを試用するために,たぶんそれを入手したいでしょう。利用可能な三つの相補的なプロセッサーがあります。それらは異なるプラットホームで動き,そして異なる長所と短所を持っています。それらは,出版界において先端的なSGML電撃戦の隊形を作っています。それらを作って寄贈してくれる人々と組織へ大いに感謝したいと思います。
Jade[
http://www.jclark.com/jade]は,最初に公開されたDSSSLプロセッサーです。Win32とUnixのプラットホームで利用できる,高速のC++に基づいたプロセッサーです。それは,フリーソフトウェア界のヒーローであるJames Clark氏によって作られました。彼は,素晴らしく強力で信頼できる,フリーのSGMLソースコードと製品を生産することによって,高価格という怪獣を堅実に殺しています。Jadeは,非常に高速です。Jadeは,スタイル言語の部分集合だけを実行しますが,それはかなり利用できる部分集合です。Jadeは,どんな変形言語も実行しません。現在のJadeは,Microsoft社のRich Text Format(しかし,RTFはDSSSLレイアウトモデルのすべての機能をサポートしていません),TeX(開発中),ウェブのHTML(非標準のDSSSL拡張を介して),SGML要素としてフローオブジェクトを記述する非標準のFOT(Flow Object Tree:フローオブジェクトツリー)形式を出力します。それらの制約にもかかわらず,Jadeは優秀な製品であり,着目する価値があります。Jadeは,SGMLソフトウェア界の誰もがコードの寄稿,文書化,検査に協力するならば,強健で高速な性能を頼れる,フリーの標準ツールになるでしょう。
Copernican Solutions社はSeng[
http://www.copsol.com/]と呼ばれるJava仮想マシン〔Virtual Machine:JVM〕のためのDSSSLツール〔DSSSL implementation〕を開発しています。このツールには,どんなプラットフォームにもJVMが移植されて機能するという利益があります。Sengが公開される時,Jadeより完全であるように思われます。ここに,Alex Milowski氏からの(電子メールの内容をわずかに編集した)情報があります。
「Sengは,商用製品になるでしょう。Sengは,Javaで書かれた構成要素に基づいたDSSSL方式〔DSSSL architecture〕であり,DSSSLプロセッサー以上のものです。私たちは,スタイル言語と変形言語を最終の製品でサポートします。
「例えば,グローブビルディング〔grove building〕は,他のDSSSLエンジン構成要素〔DSSSL engine component〕から抽出された構成要素です。したがって,プロセッサーがどのようにグローブを受け取って,そしてグローブがどんな技術で作られるかにかかわらず,Sengはグローブを処理でき,要求された結果を作り出します。
「私たちも,標準化できるような,JavaでのDSSSLのための抽象的なインターフェイスの集合に取り組んでいます。それは,開発者がJavaでDSSSL応用〔DSSSL application〕を書くことができ,任意のブラウザーかプロセッサーに引き渡すことができるといった,API標準化処理〔API standardization process〕のブートストラップを試す考えです。
「現在,私たちは,Jigsaw Javaベースのウェブサーバーと統合されたSengを持っています。したがって,Sengはブラウジングのためにだけでなく,ウェブによってSGMLを操作するためにもあります。また私たちは,最終の製品でXMLをサポートする計画を立てています。」
Emacs[
http://www.cs.indiana.edu/hyplan/wmperry/emacs.html]は,人気があるテキストエディター兼ワードプロセッサー兼ウェブブラウザー兼メールプログラム兼ニュースリーダーです。なぜDSSSLプロセッサーではないのでしょうか? Emacs(Emacs-W3)のウェブブラウザーを開発したWilliam Perry氏は,EmacsのためのDSSSLプロセッサーに取り組んでいます。Emacsのプログラムを記述したElispプログラミング言語はDSSSL表現言語に似ていますので,それはかなり自然の成り行きです。実際に彼は,Emacs-W3の将来のバージョンで,カスケーディングスタイルシートコード〔Cascading Style Sheet code〕からDSSSLスクリプト〔DSSSL script〕への即時変換を介して彼のウェブブラウザーでウェブのカスケーディングスタイルシートのサポートを実行する計画[
http://anthrax.physics.indiana.edu/gnudocs/xemacs-19.13/w3/w3_62.html]を立てています。これは,スタイルシートに関連したHTML文書やSGML文書をEmacs-W3ブラウザーで表示できるようにします。もう一度,これが完全になる時,驚くべきフリーソフトウェア製品になる約束です。コードの寄稿か文書化を考えて下さい。
DSSSLを始めているほとんどの人々は,SGML文書を印刷するか,ウェブでそれらを出版したいと思っています。これは,DSSSLスタイル言語〔DSSSL style language〕の構成要素についての学習を始める上で自然に思われます。
どのスタイルシート言語〔style sheet language〕も,(ソース文書〔source document〕から)構造的な要素〔structural element〕を書式付けオブジェクト〔formatting object〕へ対応付けする〔to map〕一連の文から成り立っています。Word for Windowsにおけるスタイルメニューも,そのような対応付け〔mapping〕から成り立ちます。それは,段落の要素を受け入れ,それらにフォント,色,その他の印刷上の効果を対応付けします。LaTeXユーザーと専門的な出版社は,このスタイルシートについての考えにたぶん非常に満足しているでしょう。
DSSSLスタイル言語において,対応付けを設定するための構文は組立規則〔construction rule〕と呼ばれ,それがSGML文書から書式付けした文書を組み立てます。
次の文が入力SGML文書〔input SGML document〕でpara要素を見つけた時に,出力書式付けしたフローオブジェクトツリー〔output formatted flow object tree〕にparagraphフローオブジェクトを作るべきであると,DSSSL応用に知らせます。
(element para (make paragraph font-size: 12pt )) ; セミコロンの後は注釈です。
フローオブジェクトは,実際には段落〔paragraph〕,表〔table〕,表現域〔area〕,数式〔mathematical formula〕などのような印刷構成体〔typographic construct〕である書式付けオブジェクト〔formatting object〕です。フローオブジェクトは,特性〔characteristic〕を持ちます。ここで,paragraphフローオブジェクトのためにfont-size特性を指定しました。それは段落のフォントサイズを(驚くべきことに)確定します。私たちは,スタイルシート全体にわたって度々このパターンを見ることになるでしょう。組立規則とフローオブジェクトは,DSSSLスタイル指定〔DSSSL style specification〕の中心にあります。
DSSSLエンジン〔DSSSL engine〕の開発者〔implementor〕がフローオブジェクトを追加することも可能です。Jadeは,特にSGMLを出版するために設計されたフローオブジェクトを追加しています。それは,TEIからHTMLへの変形のようなSGML変形を行なうために利用できます。変形を行なうためのこのスタイル言語拡張〔style language extension〕は,より実際のDSSSL変形言語〔DSSSL transformation language〕に比べて非常に異なります! 実際の変形言語は,制御の異なる流れを用い,異なるDSSSL機能〔DSSSL feature〕を利用できるようにします。スタイル言語は,追加されたフローオブジェクトと同じくらい,SGML変形のためには最適でありませんが,PerlやC++のような代わりのものと比べると確かに合理的です。これらのフローオブジェクトを用いることによって,DSSSL(少なくともJade)は,入手可能なものとして,ウェブ上の出版〔publishing on the Web〕に役立ちます。
(element para (make element gi: "P")) ; HTMLの段落を作ります
これは,入力SGML文書のP要素をpara要素に対応付けします。
見てのとおり,DSSSLの初めは全く単純です。これが難しいかもしれないかと心配していたでしょうね!舞台裏へ続く運命があります。章を変えて,これらの単純な組立規則から全く複雑なスタイル指定に当然進めることを学びましょう。今のところは,DSSSLは私たちのための仕事について後で大いに働きます。私たちは,DSSSLが自動的に行なっている二三のことに対する制御を手に入れるでしょう。
DSSSLスタイルシートのための技術的な用語は,DSSSL指定〔DSSSL specification〕です。より技術的な用語は,DSSSL指定文書〔DSSSL specification document〕がその中に複合的なスタイルシートを実際に持つことができ,(変形のように)スタイルと関係がない他の構成要素も持つこともできるという事実を表わしています。
DSSSL指定は,実際のSGML文書です。SGMLの注釈〔comment〕と実体〔entity〕はそこで立派に働きます。もちろんそれらは文書型定義に適合しなければなりません。私たちの目的のために,style-sheet.dtdを入手し,スタイルシートと同じディレクトリーにそれを入れる必要があります。公開識別子〔public identifier〕“-//James Clark//DTD DSSSL Style Sheet//EN”を介してそれを参照することもできます。
一つ以上の文書型定義に適合できるように,DSSSLスタイル指定は実際的な文書方式〔document architecture〕ですが,James Clark氏のDSSSLスタイル指定は利用しやすいものです。
スタイル言語では,二三のフローオブジェクトの名前を学ぶことによって,さらにHTMLへのそれらの対応付けを一覧にするによって,単純な文書型定義のための単純なスタイルシートを作り,組立規則を介して印刷することが容易にできます。ここにそれだけを行なう単純な文書型定義,サンプル文書,スタイルシートがあります。
見てのとおり,それはHTMLの部分集合です。HTMLは,よく知られている文書型定義なので選びました。HTMLの小さい部分集合から始めて,そのすべてに向けて取り組みましょう。私たちは,それが出版文書〔publishing document〕に使用された普通の文書型定義であるかのようにHTMLを取り扱い,それを印刷するためのスタイルシートを作ります。より構造化された文書型定義も後で見ます。それは,HTMLをHTMLへ変形するためのスタイルシートを作ることではありません。そうしたJade拡張を無視して,印刷について集中することになるでしょう。原則のほとんどは同じです。
ここに,Jon Bosak氏によって書かれた,より包括的なHTMLスタイルシートに基づいた小さいスタイルシートがあります。
<!DOCTYPE HTMLLite [ <!ELEMENT HTMLLite O O (H1|P)*> <!ELEMENT (H1|P) - - (#PCDATA|EM|STRONG)*> <!ELEMENT (EM|STRONG) - - (#PCDATA)> ]> <HTMLLite> <H1>これは見出しです</H1> <P>これは文です</P> <P>これは<em>ボールド体</em>です</P> <P>これは<strong>強調体</strong>です</P> </HTMLLite>
<!DOCTYPE style-sheet system "style-sheet.dtd" > <!-- これを正確に解析するにはJames Clark氏のstyle-sheet.dtdがなければなりません。 --> (element HTMLLite (make simple-page-sequence)) (element H1 (make paragraph font-family-name: "中ゴシック体" font-weight: 'bold font-size: 20pt line-spacing: 22pt space-before: 15pt space-after: 10pt start-indent: 6pt first-line-start-indent: -6pt quadding: 'center keep-with-next?: #t)) (element P (make paragraph font-family-name: "細明朝体" font-size: 12pt line-spacing: 13.2pt space-before: 6pt start-indent: 6pt quadding: 'start)) (element EM (make sequence font-posture: 'italic)) (element STRONG (make sequence font-weight: 'bold))
特別のheadingフローオブジェクトがなく,その代わりにparagraphフローオブジェクトを用いていることに注意して下さい。paragraphの特別のスタイルとしてheadingを定義する慣例は,デスクトップパブリッシングとワードプロセッシングのソフトウェアに共通です。headingは,paragraphフローオブジェクトの特別な形態です。HTMLへ変換するための,前述した特別のHTMLフローオブジェクトをJadeが用意していることは,その理由の一つです。他の文書型定義からHTMLへ変形する場合,正確なHTML表示のためのheadingとして headingを正確に分類できる必要があります。
残りのすべてを含んだmainフローオブジェクトは,印刷のためのsimple-page-sequenceフローオブジェクトまたはpage-sequenceフローオブジェクト,そしてオンライン〔on-line〕のためのscrollフローオブジェクトでなければなりません。
特性名〔characteristic name〕のほとんどは,かなり分かりやすいものです。次は,それらの要約の一部を挙げます。
font-family-name:
フォントファミリー名〔font family name〕,または,どんなフォントもを意味するトークン〔token〕の#f。Jadeにおいて,指定されたフォント名〔font name〕は出力文書(.rtf,.texなど)に複写され,それを見つけるのは印刷応用〔printing application〕の責任になります。
font-size:
期待どおり,フォント縮小の量〔quantity〕。10ptのように,それはいつもポイント(pt)で指定します。ミリメーター,インチ,パイカなどに関しても指定できます。
line-spacing:
(左から右へ書く場合の)ある行の最下部から他の行までの間隔〔space〕の量。
space-before:
最後の段落の後にある段落の前に挿入された間隔のような,表現域〔area〕の前の間隔。
start-after:
次の段落の前にある段落の後に挿入された間隔のような,表現域〔area〕の後の間隔。1番目の表現域のspace-afterと2番目の表現域space-beforeの両方に依存する,表現域(例えば,段落)の間にある実際の間隔。
start-indent:
左から右へ書く場合(例えば,英語),文の左マージンと1ページの左マージンとの間の間隔のような,表現域での字下げ〔indent〕。
quadding:
ワードプロセッサーは,クワタ〔quadding〕のことをjustificationまたはalignmentと呼んでいます。start,end,center,justifyが妥当な値です。
特性名の完全な一覧についてはDSSSL指定を参照して下さい。一重引用符“’”は,それに続く(startのような)文字列がシンボル〔symbol〕であることを表わします。 シンボルの終端を閉じる引用符は使えません。シンボルについては後述します。より詳細な情報については,DSSSL指定を調べるべきです。
実際にこのスタイルシートを使ってサンプル文書を検査してもかまいません。次のステップは,HTMLの部分集合をもう少し大きくサポートするために,いっそう多くのheadingの型(H2からH6)を持てるように,このスタイルシートを拡張することです。そこで,DSSSLでスタイルシートコードを再利用する〔to reuse〕ための機構について述べます。
いっそう多くのheadingレベルを含めるために,私たちの出版に適したHTML部分集合を広げたいと思います。それを行なう分かりやすい方法は,H1のための定義を元にしてfont-sizeやquaddingなどを変更し,H2からH6の定義にすることです。しかし,それより優雅に行なえる機能をDSSSLは持っています。例えば,それらすべてを変更するために,フォントの一つを変更したいと思うでしょう。それらすべてを独立して変更すべきではありません。font-family-name特性と他の特性の定義を再利用することができればいいわけです。典型的なSGML文書であれば,DSSSLスタイルシートは,実体〔entity〕を用いているコードを再利用するためにSGML文書であるという事実を,私たちは利用できます。しかし,DSSSL定義がそれより柔軟で強力であるということが分かるでしょう。
(define *heading-font* "中ゴシック体") (define *heading-weight* 'bold) (define *heading-posture* 'italic) (element H1 (make paragraph font-family-name: *heading-font* font-weight: *heading-weight* font-posture: *heading-posture* font-size: 20pt quadding: 'center)) (element H2 (make paragraph font-family-name: *heading-font* font-weight: *heading-weight* font-posture: *heading-posture* font-size: 18pt quadding: 'left))
技術的用語において,最初の定義は,識別子〔identifier〕*heading-font*を文字列“中ゴシック体”に束縛〔binding〕します。これは,SGML実体〔SGML entity〕を思い起こさせるかもしれません。先頭と末尾にアステリスクのある変数名〔variable name〕を用いたのは,変数名としては何でも名付けられるということを強調したかったからです。それらは変数の任意の名前で,値を束縛できます。ここで,なぜ一重引用符がシンボルに先立つことが重要であるかが分かるでしょう。そうでなければ,それらは変数として解釈されてしまうからです。DSSSLプロセッサーはそれらを変数として調べ,エラーであるどんな値も持っていないということを知ります。
通常のプログラミング言語では,次のように書くでしょう。
let x = 5
これは,識別子xを参照し,それに値5を束縛する意味です。定義の目標は,コードの重複を減少させることです。“中ゴシック体”とする代わりに,それに束縛された識別子だけを指定できます。font-family-name特性への変更がすべてのheadingを同時に変更します。
しかし,font-sizeの場合はどうでしょうか? どのheadingも同じfont-size特性を持たないようにしても,それらの間には関係が確かにありますので,その関係を表す必要があります。それを行なう前に,式〔expression〕のDSSSL概念を検討しなければなりません。
ずっと前に式を見たことがあっても,今はそれらを思い出せないかもしれませんね。たぶん高校数学でそれらを勉強したでしょう。
すでに例の中に多くの式を見ました。その一つを調べてみましょう。
(element EM (make sequence font-posture: 'italic))
この組立規則にはいくつかの式があります。
数式は任意に(必要に応じて)深く入れ子にできる,と思うかもしれませんね。DSSSL式もそうです。しかし,DSSSL式と数式〔math expression〕との間には大きな違いがあります。
数学において,(+,-,*のような)演算子は,それが働くものの間に置かれます。これは挿入辞表記法〔infix notation〕と呼ばれています。それで,5 + 5は二つの5の間に挿入されます。挿入辞表記法は,正規の数学のためには結構ですが,大きな式を表示するには比較的複雑な印刷上の手法が要求されます。上下の部分を表わすために非常に大きく開いた行を用いたり,どちらかにある二つの複雑な式を加えるためにページの中央に“+”を用いたりする複雑な式を見ることになります。私たちは,DSSSL指定を作ることにおいて装飾的なスタイルを持っていません。複雑な式を作るだけのために特別な式編集ソフトウェアを要求すべきではありません。DSSSLが用いている表記は,括弧でくくった接頭辞表記法〔parenthesized prefix notation〕と呼ばれています。そして,DSSSL指定を作る人々は,特別なソフトウェアなしに式の構造を表わすために,特別なレイアウト形式を用います。ここに,DSSSLユーザーが用いている慣習による接頭辞表記式へ翻訳された挿入辞表記式〔infix expression〕のいくつかの手本があります。
| infix | prefix |
|---|---|
| (1 + 3) | (+ 1 3) |
| (1 + (3 + 5)) | (+ 1 (+ 3 5)) |
| ((1 * 3) + (5 * 4)) | (+ (* 1 3) (* 5 4)) |
| x = (((1 * 3) + (5 * 4)) + (((3 * 6) + (7 * 8)) / 2)) | (define x (+ (+ (* 1 3) (* 5 4)) (/ (+ (* 3 6) (* 7 8)) 2))) |
注)
実際にそれに慣れてしまえば,DSSSLの構文は伝統的な数学表記法より快適かもしれません。前述したような式を作っている時に,たぶん次のように考えるでしょう。
DSSSLは,最初に操作〔operation〕について考え,後で演算数〔operand〕について考える方法をサポートします。他方では,人気がある高価なHP計算器〔HP calculator〕が大好きな人々は,DSSSLの表記法の正反対である接尾辞表記法〔postfix〕を好んでいます。それは表記法が独断的であるということを示しています。それらのいずれにも慣れるはずです。演算子がその引数(演算数)の間で置かれた,と単純に想像するだけで,それと等価の挿入辞表記式へほとんどの接頭辞演算子を変えることができます。
主流のプログラミング言語が複雑な入れ子にされた式の問題をどのように解析するかと思うかもしれませんね。明らかに,挿入辞表記法は,大きな式ではかなり扱いにくいものです。式が複雑になる場合,C,C++,Basic,Javaのような手続き型の挿入辞表記法のプログラミング言語では,一つの変数へ式の成分を,さらに別の変数へ別の成分を割り当てることが求められます。そして,変数を伴っている式と割り当てを通して複雑な式を作り上げます。表現言語は,式を作って入れ子にすることを認めるスタイル言語の部分です。見てのとおり,表現言語において変数なしで複雑な式を作れることは便利なことです。
サンプルのスタイルシートに戻りましょう。ここで,どのように変数を定義し,どのように式を作るかを知ったことになります。さらに,headingがサイズを除けば同じで,しかもそれらのサイズが関連しているという考えを表すことができます。
(define document-font-size 10pt) (define heading-font "中ゴシック体") (define heading-weight 'bold) (define heading-posture 'italic) (element p (make paragraph font-size: document-font-size)) (element H1 (make paragraph font-family-name: heading-font font-weight: heading-weight font-posture: heading-posture font-size: (* 2 document-font-size); 文書のフォントサイズの2倍にします。 quadding: 'center)) (element H2 (make paragraph font-family-name: heading-font font-weight: heading-weight font-posture: heading-posture font-size: (* 1.8 document-font-size) ; H1より小さくします。 quadding: 'left))
これですべてを組み立てたことになります。font-size特性を指定するために式を用いました。文書のフォントサイズを表わすために定義した変数について式を配置しました。そして,各々のheading型について異なる乗数を定義済みのフォントサイズに乗ずるために,標準のDSSSL接頭辞表記法を用いました。他のスタイルシート言語で困難か不可能である多くのことをすでに行ないましたが,DSSSLは始動状態になっているだけです。フローオブジェクトを操作するために,いくつかの先進のツールについて説明し,さらに,どのようにこれらのheadingがより多くのコードを共有できるかについて考えてみましょう。
ここまで。私たちは,paragraphとsequenceのようなフローオブジェクトを扱ってきました。まだ説明していないことは,sosofoで取り扱ってきたことです。「え,sosofoって何?」sosofoとは信じようが信じまいが,(コンピューター産業か軍隊だけがその一つを思いつくことができる)頭字語です。しかし,実際にそれは非常に合理的な頭字語です。sosofoは,「フローオブジェクトのシーケンスの指定〔Specification Of a Sequence Of Flow Objects〕」を意味します。これは発音しにくい語ですが,他のコンピューター産業の頭字語と違って少なくとも非常に説明的です。私たちはsosofoを見ることができて,それがどのように「フローオブジェクトのシーケンスの指定」であるかを理解できます。たぶん最も単純なsosofoを作るコードは,次のものでしょう。
(make paragraph font-size: 12pt)
このコードがフローオブジェクトを作ると前述しました。それはさらに簡略化〔simplification〕したものです。実際にこのコードはフローオブジェクトのための指定を作ります。フローオブジェクトの青写真を作るように考えます。また,paragraph内にある文を修飾したり,フローオブジェクトに名札〔label〕を付けたりすることもできます。組立規則の実行が終わった時,DSSSLエンジンは青写真としてのsosofoを解釈し,paragraphを作ります。青写真(sosofo)と製作(フローオブジェクト)との間の区別が後でより重要になるでしょう。DSSSLのより強力な機能について学ぶ時,複雑な論理に基づくsosofoを作ったり複製したり無視したりできるようになるでしょう。ちょうど建物についての物理的な青写真を作ったり複製したり無視したりできるようにです。DSSSLスタイルシートの実行が終わった後に,フローオブジェクトが作られるので,フローオブジェクトを複製したり破棄したりすることはできません。
フローオブジェクトの非常に短いシーケンスのための指定を生成するように見える,make式〔make expression〕と呼ばれる前述したコードに注目して下さい。実際にparagraphの中のすべての文字がDSSSLエンジンによって自動的に処理されますが,それらはcharacterフローオブジェクトを指定するsosofoを返却します。また,paragraphの(<em>要素のような)部分要素〔subelement〕は自動的に処理されますが,私たちはその状況を制御できます。DSSSLはそれらの子供要素〔child elements〕を自動的に処理しますが,私たちがそれを行なうことができます。
(make paragraph font-size: 12pt (process-children))
make式の内側で,見出し語引数並び〔keyword argument list〕の後に,何をフローオブジェクトに入れるかをDSSSLプロセッサーに指示する,内容式〔content expression〕を指定できます。この場合に,その要素の子供ノード〔child node〕(通常は子供要素と個々の文字ノード)を処理し,paragraphフローオブジェクトの内容のためにそれらを用いるようにDSSSLプロセッサーに要求します。内容式を指定しない場合,作られているフローオブジェクトが子供を持てない場合でない限り,DSSSLは自動的に(process-children)を用いるでしょう。そのようなフローオブジェクトは原子フローオブジェクト〔atomic flow object〕と呼ばれます。characterフローオブジェクトは一つの例です。それはどんな内容も持っていないかもしれません。文字自身はchar特性です。
今までのところで,DSSSLが仕事を行なうようにしてきました。それは内容式(process-children)として書いてありますが,私たちは子供ノードの処理を制御できます。では,なぜそれを行ないたいのでしょうか? たぶん,何人かの子供に対する処理を抑えたり,個別の一つだけを処理したりしたいでしょう。または,たぶん,元の文書において要素に直接対応していない,いくつかのフローオブジェクトを取り入れたいでしょう。
例えば,次の例で,私たちは架空の文書型定義で読者に警告を表わすnote要素を扱いたいと思います。
<NOTE>底の厚い婦人靴お断り!</NOTE>
それは次のように表示されるでしょう。
警告:底の厚い婦人靴お断わり!
make式には,いくつかの部分があります。次は,DSSSL指定からの完全な定義です。
make式 = (make フローオブジェクトクラス名 見出し語引数並び 内容式*)
この定義におけるアステリスクは,必要とする数だけの内容式が持てることを意味します。この例でこの機能を用います。
語“警告:”を表示する文字の並びを作ります。それらをボールド体にするsequenceフローオブジェクトでそれらを囲みます。note要素の実際の内容を処理する内容式も持つでしょう。一つのparagraphの中でそれらの両方を囲みます。
(element note (make paragraph font-size: 12pt (make sequence font-weight: 'bold (literal "警告:")) (process-children)))
literal手続きは,文字列の文字を表わしているcharacterフローオブジェクトのシーケンスを指定しているsosofoを作ります。この場合,それはいくつかのcharacterフローオブジェクトのシーケンス(“警”,“告”,“:”)を指定しているsosofoを作ります。このliteralは,font-weight: 'bold特性のあるsequenceフローオブジェクトのためのmake式の範囲内にあるので,これらの文字はボールド体にされます。
literalを含んでいる,ボールド体にされたsequenceのためのmake式が(process-children)手続きに先行しているので,sequenceは,paragraphの子供によって生成された他の内容に先行するでしょう。また,新しいparagraphを作らないでsosofoを(接着剤でくっつけるように)付け加えることは可能です。次の例は,三つのparagraphを作ります。
(element note (make sequence (make paragraph font-size: 14pt font-weight: 'bold (literal "注意:")) (make paragraph font-size: 12pt font-weight: 'medium (process-children-trim)) (make paragraph font-size: 14pt font-weight: 'medium (literal "あなたは警告した!"))))
sequenceが最初のsosofo(ボールド体にされた語“注意:”)と2番目のsosofo(note要素の子供)と最後の文を連結するフローオブジェクトを作ります。見てのとおり,式を入れ子にするDSSSLの能力がうまく働いています。
(process-children-trim)は(process-children)の単純な変化形です。それは要素の内容の始めと終わりにおける空白〔whitespace〕(通常は,間隔〔space〕,改行〔newline〕,タブ〔tab〕など)を取り除きます。
(empty-sosofo)内容式は,空のsosofoを返却します。要素を抑えるために,これを利用できます。
(element hidden (empty-sosofo))
すべての要素の子供を処理しないようにすることは可能です。例えば,文書の目次を生成するスタイルシートを書きたい場合を想定します。その場合に,表示すべき子供(章の表題)と表示すべきでない子供(それ以外のすべて)を正確に指定できる,より進んだ内容式を必要とするでしょう。process-matching-children手続きを用いてこれを行ないます。
(process-matching-children 'section)
process-matching-children手続きの引数は,パターンと呼ばれます。パターンと一致するどんな子供要素も処理されます。この場合,それはすべてのsection要素です。用いられた組立規則は,いつも用いられるものと同じです。もちろんそれは,titleが組立規則(process-children)を持っている場合に,またはtitleが(process-children)を暗示させるどんな明白な組立規則も持っていない場合に,titleの子供要素が処理されることを意味します。
要素名“section”が一重引用符文字“’”の後のシンボルによって表わされることに注目して下さい。その引用符を含めないと,DSSSLインタプリターは,変数として“section”を調べようとします。
process-matching-children手続きは,一つ以上のパターンを扱うことができます。それは一つ一つのパターンを試して,どれかで一致する子供を処理します。この例は,すべてのsection子供要素とappendix子供要素を処理します。
(process-matching-children 'section 'appendix)
また,任意の方法で扱うこともできます。
process-first-descendantと呼ばれる,process-matching-childrenにやや似かよった別の手続きがあります。それが線型のSGML文書の後方にツリーを伸ばし,その引数と同じ名前である最初の要素を探すことが想像できるでしょう。
(process-first-descendant 'title)
これは,現要素〔current element〕の最初の子供がtitleならば,さらに調べ,その子供がtitleである子孫〔descendant〕を持っているかどうかを調べます。そうでなければ,現要素の2番目の子供がtitleならば,さらに調べ,その子供がtitleである子孫を持っているかどうかを調べます。これを続けます。
現在の文書において識別子〔identifier〕がどこに生じても,特定の唯一の識別子を持った要素を処理することができます。これは相互参照〔cross-references〕の処理でよく利用されます。例えば,(titleのような)参照符を付けられた要素の文を必要とするかもしれません。次の文書を想定して下さい。
<document> <section> <title id="Intro">はじめに</title> <p>… <p>… </section> <section> <title>おわりに</title> <p>さて <title-ref idref="Intro">で述べたように,… </section>
title-refの箇所でtitleから内容を取り出したいと思います。どのtitleへの参照かを教えてくれるtitle-ref属性は,この文書型定義においてidrefと呼ばれます。(それは,しばしば目標〔target〕とか単にidと呼ばれます。)
(element title-ref (process-element-with-id (attribute-string "idref")))
それで,titleはtitle-refが生じる箇所で処理されるでしょう。
この例では,二つのことを意図しています。最初は,ID属性はtitleにおいてよりもむしろ親要素であるsectionにおいて実際に生じるという,かなり良い機会があります。結局,それはtitleではなく,今識別しているsectionです。2番目は,titleの組立規則が,大きくボールド体にされ中揃えされたparagraphを実際に作るという,かなり良い機会があります。それは,title-refの箇所に挿入したいものではありません。次にモードを勉強する際に,これらの問題の両方を訂正することになるでしょう。
いくつかの異なる組立規則を用いている文書のsection(またはすべて)を処理したい場合,DSSSLは,処理モード〔processing mode〕と呼ばれる,それを行なう単純な方法を備えています。例えば,たぶん前述した参照問題を扱いたいと思うでしょう。sectionへの参照を処理したいでしょうが,sectionのtitleに特別の書式付けをしないで印刷だけを行ないたいと思うでしょう。
「titleだけですよ,お客さん」
モード組立規則群〔mode construction rule group〕と呼ぶものへ特別の組立規則を分類することによって,これを行ないます。ここにモード組立規則群の例があります。
(element title (make paragraph font-size: '20pt quadding: 'center)) (mode section-reference (element section (make sequence (literal "■ ") (literal (format-number (child-number) "1")) (process-first-descendant 'title))) (element title (process-children)))
処理していた要素がその親要素〔parent element〕の特定の型を持つ最初の要素であれば,child-number手続きは1を返却し,2番目であれば2を返却し,以下同様であるということに注意して下さい。format-number手続きは,リテラルの内容として利用できる文字列〔string〕に数〔number〕を変えます。2番目の引数は,用いられた形式(アルファベット〔alphabetic〕,ローマ数字〔roman〕,アラビア数字〔arabic〕)を指定します。ここでは,アラビア数字を選びました。それで,このsection組立規則は,語“■ ”とそのchild-numberを印刷します。その最初のtitle要素を探して処理します。
titleがsection-referenceモードで処理されていますので,初期モード〔initial mode〕の組立規則(特定の名前を付けたモードの外側にある組立規則)よりもむしろ,そのモードの範囲内で指定された組立規則が用いられます。名前を付けたモードの組立規則が存在しなかった場合,要素は,その代わりに初期モードからの組立規則によって処理されます。初期モードからの組立規則を「模倣する〔to borrow〕」(または「継承する〔to inherit〕」)と言います。それで,その最初の子孫〔descendant〕だけの処理を強制することは重要です。そうでなければ,すべての他の子孫からの内容は,初期モードにおけるそれらの組立規則によって返却されます。この「section参照」は,sectionのすべての内容を含んでいます!
モード名〔mode name〕は,基本的に任意です。(変数名や手続きのような)どんな他の任意の名前のように,思い出しやすい名前をモードに付けることは容易です。モードを利用するためには,その名前を用いるwith-mode式を作ります。
(element section-ref (with-mode section-reference (process-element-with-id (attribute-string "idref"))))
これは,section-referenceモードにおいてidrefの他の終了でsectionを処理します。また,多くの異なる種類の要素を参照できる一つの参照要素〔reference element〕を作れることが想像できるでしょう。より直観的であるsection-referenceモードの名前を(たぶんany-referenceに)変更でき,各々の参照目標〔reference target〕のための組立規則を追加できます
文書の全体かsectionについて代替的書式付け〔alternate formatting〕を作るために処理モードを用いる際に,処理モードはさらに強力です。例えば,各々の最上位〔top-level〕sectionのtitlesのために目次を生成したい場合を想像してみましょう。これから行なうことによって,最上位のsectionのheadingだけを印刷するモードが設定されます。それはany-referenceモードのようにはなりますが,各々のsectionについて,それは,section自身のtitleだけというよりもむしろsectionのtitleを含むparagraphを出力するでしょう。そうでなければ,目次は一つの長い行となるでしょう!
(mode toc (element section (make paragraph (literal "■ ") (literal (format-number (child-number) "1")) (process-first-descendant 'title))) (element title (process-children)))
referenceモードがあるので,このモードが大いに利用できますが,(process-element-with-id)よりむしろ(process-children)を用います。文書のルート要素〔root element〕(他のすべてを含む要素)は,documentと呼ばれると仮定して下さい。
(element document (make sequence (process-matching-children 'title) (make paragraph font-size: 18pt (literal "目次")) (with-mode toc (process-matching-children 'section)) (process-children) ))
最初に,この組立規則は,documentのtitleを印刷します。次に,それは,文“目次”を印刷します。次に,それは,title文だけを得るためにany-referenceモードのdocumentの各々のsectionを処理します。最後に,それがinitialモード(正規)でdocument全体を処理します。また,section,subsection,sub-subsectionsなどのすべてにおいて,一つの再帰的な〔recursive〕目次を作り上げるモードにすることができます。この課題をより容易に解決できる名札付きsosofo〔labelled sosofos〕と呼ばれるDSSSL機能がありますが,その機能はこの手引では説明しません。
SGML についてよく知っている方は,比較的単調なHTML文書よりも複雑な構造をどのように扱うのかを知りたいでしょう。その代わりに,TEI文書型定義に基づいた入れ子になったsectionの例を見てみましょう。
<!DOCTYPE root [ <!ELEMENT root - - (head, div+)> <!ELEMENT div - - (head, (div+ | p+))> <!ELEMENT head - - (#PCDATA)> <!ELEMENT p - - (#PCDATA)> ]> <root> <head>文書のtitle</head> <div> <head>レベル1(最上位)のsectionのtitle</head> <div> <head>レベル2(レベル1の下位)のsectionのtitle</head> </div> <div> <head>レベル2のsectionのtitle</head> </div> <div> <head>レベル2のsectionのtitle</head> <div> <head>レベル3(レベル2の下位)のsectionのtitle </head> </div> </div> </div> <div> <head>レベル1のsectionのtitle</head> </div> </root>
この文書型定義においては,同じ要素型のheadがそれらのすべてについて使用されるという事実にもかかわらず,外見が異なって見える異なるレベルでのtitleを作る方法を必要とします。そのためのsosofoを作る以前に,headingの深さを調べる条件付きのDSSSLコードを書くことはできますが,それを行なう必要はありません。DSSSL標準は,これを行なうために単純で洗練された機構を備えています。例えば,sectionのheading(DIVのHEAD)と異なるように見えるdocumentのheading(ROOTのHEAD)を作る場合に,その方法で行なえます。
(element (DIV HEAD) (make ...) ) (element (ROOT HEAD) (make ...) )
最初は,DIV要素におけるHEAD要素のための直接的な組立規則です。2番目は,ROOT要素におけるHEAD要素と直接一致します。必要に応じて,この祖先の鎖〔chain of ancestors〕を作れます。subsection,sub-subsectionなどを扱うために,次のような規則を作れます。
(element (DIV DIV HEAD) (make ...) ) (element (DIV DIV DIV HEAD) (make ...) ) (element (DIV DIV DIV DIV HEAD) (make ...) )
すべてのHEADを特徴付けるための条件付きコードで,それらのすべてを扱う一つの規則を持つことは,より洗練されているように思われるかもしれません。私たちにはその選択肢がありますが,sectionのheadとsub-sectionのheadは,すでに定義を介して共有できたものの他に,コードをほとんど共有しないほど,しばしば根本的に違います。これについては後述します。
(process-matching-children) 手続きと違って,組立規則は属性値〔attribute value〕に基づいて選べないことに注意して下さい。複雑な要素区別〔element differentiation〕を行ないたい場合は,質問機能をサポートするDSSSLツールを用いなければなりません。質問組立規則〔query construction rule〕を用いて,親の性状,子供の存在などといった,属性に基づく複雑な質問を行なうことができます。Jadeは質問機能をサポートしていません。しかし,より多くの仕事で同様のことを行なうために基本的に条件式を利用できます。それについては後述します。
前述したとおり,フローオブジェクトは,SGML文書へのスタイルシートの応用から結果として生じる書式付け構造体〔formatting constructs〕です。ここまでは,paragraph,sequence,simple-page-sequenceという三つのフローオブジェクトを用いただけでした。また,simple-page-sequenceフローオブジェクトとpage-sequenceフローオブジェクトの箇所においてオンラインで使用された,simple-page-sequenceフローオブジェクトが行なうよりも複雑なレイアウトを可能にするscrollフローオブジェクトのことについても言及しました。これらのより詳細な情報については,DSSSL指定を参照して下さい。
それらのフローオブジェクトを持った,非常に合理的なスタイルシートが作れます。多くのスタイルシート言語は,それ以上のものを持っていませんが,DSSSLは,豊富なフローオブジェクトを備えた道具箱〔toolbox〕とそれらを結合させるいくつかの面白い方法を持っています。ここで,有用かもしれない二三のことについて簡潔に触れます。より詳細な情報についてはDSSSL指定を参照して下さい。
line-fieldフローオブジェクトは,文の行でfixed-widthのfieldを作ります。番号付きの箇条書〔numbered list〕の番号〔number〕のためにそれを用いるかもしれません。ここで,番号やブレットの後にある最初の文字の開始は,番号やブレットのサイズに基づくべきではありません。
<!DOCTYPE style-sheet system "style-sheet.dtd"> (root (make simple-page-sequence)) (element item (make paragraph (make line-field field-width: 12pt (literal (format-number (child-number) "1")) (literal ".")) (process-children)))
display-groupフローオブジェクトは,sequenceフローオブジェクトが行なうのと同じ方法で,他のフローオブジェクトを(接着剤のように)連結しますが,それは新しい表示表現域〔display area〕を作ります。表示表現域はインライン〔inline〕でない表現域です。インライン表現域〔inline area〕は,それらに先行し続く表現域と同じ行に表示されます。表示表現域は新しい行から始まり,他の表示表現域は新しい行から続きます。図や箇条書は,display-groupであるかもしれません。つまり,space-before特性とspace-after特性を持った文書の残りからそれらを引き立たせることができます。あるいは,同じページにすべての項目〔item〕を置くことを強制するkeep特性を利用できます。
Jadeは,HTML出力のために特にproprietaryフローオブジェクトを持っています。それは書式付け指令〔formatting-instruction〕と呼ばれ,次のように利用されます。
(element P (make sequence (make formatting-instruction data: "<P>") (process-children) (make formatting-instruction data: "</P>")))
書式付け指令は,HTMLファイルを構成するscrollフローオブジェクトにdata特性(この場合,“<P>”と“</P>”)の文字列を複製します。それはJadeのページで文書化されます。page-sequenceの代わりにscrollフローオブジェクト内に書式付け指令を置くことを忘れてはなりません。
DSSSLの表現言語〔expression language〕は,他のプログラミング言語ができることのほとんどを行なえる十分な特徴を備えたプログラミング言語です。しかし,言語としては副作用〔side-effect〕はありません。つまり,SGML文書を変形したり書式付けこと以外の,ファイルを読んだり書いたり,ウインドウを開いたり閉じたりするといったことはできません。
表現言語では,#tが真〔true〕を,#fが偽〔false〕を意味します。それで,2が3より小さいことを決める式(< 2 3)は,#tに評価されます。3が2より小さいことを決める式(< 3 2)は,#tに評価されます。#tと#fの値をブール〔booleans〕と呼びます。DSSSLの専門家は,ブールを返却する演算子を,大学の哲学か数学の課程で学ぶ「述語論理」からの用語で「述部」と呼びます。
演算子>は,最初の引数が数値的に2番目より大きければ真(#t),そうでなければ偽(#f)となるブールを返却することに注意して下さい。他の述部は,数の等値(=),奇数(odd),偶数(even)といったことや,特定の要素が一連の最初であるかどうか(first-sibling?)といったような,数とは全く何の関係もないことを調べます。
条件式は,一つの部分式〔sub-expression〕または条件に基づく別の部分式を実行する式です。他のプログラミング言語でもプログラムを作ったことがあれば,表現言語のif構文〔if construct〕が他の言語のif/then/elseによく似ていることが分かるでしょう。表現言語では,ifは次のように見えます。
(if test consequent alternate)
最初にtestが評価されます。testが真値〔true value〕を生じる場合,consequentが評価され返却されます。そうでなければ,alternateが評価され返却されます。表現言語では,トークン#f以外のどんな式も,空リスト〔empty list〕“'()”,数“0”,文字列“"false"”,シンボル“'false”を含め(制限されません!),真に評価されることに注意して下さい。これが通例でない言語に精通しているプログラマーは慎重になるべきです。
動作中の条件とその返却値〔return value〕の(DSSSL標準から少し引用した)例を次に挙げます。
| (if #t 'yes 'no) | yes |
| (if #f 'yes 'no) | no |
| (if (> 3 2) 'yes 'no) | yes |
| (if (> 2 3) 'yes 'no) | no |
| (if "エルビスは生きている" "彼は生きている!" "彼は死んでいる!") | "彼は生きている!" |
| (if (> 3 2) (- 3 2) (+ 3 2)) | 1 |
最後の例は,条件式の構成要素としてより複雑な式をどのように用いるかを示しています。非常に強力で複雑な構造を作るために,この機能を使ってみましょう。ここに分かりやすい例を挙げます。一連の最初のparagraphがその前でどんな間隔も持っていなくて,別のparagraphには(paragraph間の間隔〔inter-paragraph space〕として)その前の間隔があるように,文書型定義が設計されているとします。
それを行なう分かりやすい方法は,条件式の範囲内で入れ子にした二つのmake式を持つことであるかもしれません。
(element p (if (first-sibling?) (make paragraph first-line-start-indent: 0pt) (make paragraph first-line-start-indent: paragraph-indent)))
処理されている現要素が(同じ共通識別子〔generic identifier〕の)同じ型の他の要素の後にない場合,first-sibling?手続きは,真を返却します。DSSSL標準の第10章では,これに似た多くの有用な手続きを定義しています。より複雑なDSSSLスタイルシートを作る際は,それらに精通すべきです。
これは機能しますが,より洗練された方法があります。条件式は,式ですから,それは普通の式ができるどんなこともできます。これはDSSSL表現言語の非常に強力で有用な機能です。
(element p (make paragraph first-line-start-indent: (if (first-sibling?) 0pt paragraph-indent)))
どの条件式もif部分(consequent)とelse部分( alternate )があることに注意して下さい。多くのプログラミング言語では,実行されるかされないかの一連の指令をコードとみなしますので,alternateを省略できます。表現言語は,拡張された式をコードとみなします。つまり,式は,常に値を返却しなければなりません。結局,次の式は何を評価するのでしょうか。
(if #f 'yes)
指定しなかったので,これは“'no”を評価できません。何も評価できないのです! これは論理上不完全なだけです。解決案は値を常に返却することです。どんな実際の内容も持っていない,表示もしないし間隔も印刷しないsosofoを返却したい場合は,次のような空のsofofo((emtpy-sosofo))を返却できます。
(element p (if (not (equal? (attribute-string "SECURITY") "HIDDEN")) (make paragraph) (empty-sosofo)))
今,属性値HIDDENによって隠されたparagraphは,印刷文書では見えなくなるでしょう。
また,文書の全体にわたった最上位でのトリガー〔top trigger〕によってフラグ〔flag〕を変更したように,DSSSLスクリプト〔DSSSL script〕を編成するための定義に関連した条件式を用いたいと思うかもしれませんね。
(define BIG-TEXT #t) (element p (make paragraph font-size: (if BIG-TEXT 20pt 12pt))) (element (report head) (make paragraph font-size: (if BIG-TEXT 30pt 20pt)))
条件式を生産的に用いるために,and式とor式をどのように使うかを知るべきです。and式は,0個以上の引数をとり,すべての引数が真ならば真を返却します。and式は,引数のどれかが偽であれば#fを返却します。通常は,2個の引数をもってand式を用いますが,引数がなくても用いることができます。引数がない場合と2個以上の場合は,#tを返却するだけです。
| (if (and (= 2 2) (> 2 1)) 'yes 'no) | 'yes |
| (if (and (= 2 2) (< 2 1)) 'yes 'no) | 'no |
| (if (and 1 2 'c '(f g)) 'yes 'no) | 'yes |
| (if (and) 'yes 'no) | 'yes |
or式は,0個以上の引数をとり,引数のいずかが真(#f以外でも)ならば真を返却します。or式は,引数のすべてが偽であれば#fを返却します。
| (if (or (= 2 2) (> 2 1)) 'yes 'no) | 'yes |
| (if (or (= 2 2) (< 2 1)) 'yes 'no) | 'no |
| (if (or #f #f #f) 'yes 'no) | 'no |
not手続きは1個の引数をとり,それが偽(#f)ならば#tを返却し,そして真(その他の値でも)ならば偽(#f)を返却します。
| (not #t) | #f |
| (not 3) | #f |
| (not (list 3)) | #f |
| (not #f) | #t |
| (not '()) | #f |
| (not (list)) | #f |
| (not 'nil) | #f |
DSSSL表現言語では,#fでない値は真であるとみなすことを思い出して下さい。それで,anythingが#fでなければ,実際に,(not anything)は#fです。
ここまでで,条件式〔conditional expression〕,入れ子化〔nesting〕,定義〔definition〕を伴う比較的複雑な式をどのように作り上げるかを説明しました。式を毎回タイプしないために,式を再利用できる方法があれば,それを知りたいと思うでしょう。DSSSL表現言語は,手続き〔procedure〕と呼ばれる構成体を通して式の再利用を認めます。手続きの機能と能力についての完全な記述は,この手引の範囲を超えていますが,単純な手続きは容易に行なえます。それらをDSSSLスタイルシートの中に見るでしょう。
私たちは,すでに組込み〔built-in〕手続きに取り組んできました。例えば,“+”は二つの数を加える手続きです。“*”はそれらを乗ずる手続きです。“=”,“<”,“>”は,数を比較する手続きです。
前述した適度に複雑な式は,titleを作るために用いたものです。それは問題にはなりませんが,コードは,title(documentのtitle,sectionのtitle,sub-sectionのtitleなど)の異なる種類ごとに何度も複製されなければなりません。HTMLのためのスタイルシートには6種類のtitleがあります。スタイルシートの作者がどれくらい多くのレベルのtitleを区別するかによって,他の文書型定義のためのスタイルシートでは,もっと多くのtitleを持たなければならないかもしれません。複雑な式を取り出し,それを型紙〔template〕か抽象化〔abstraction〕へ変えられれば素晴しいでしょう。
前述したデータ定義〔data definition〕によく似た手続き定義〔procedure definition〕を用いて,それが行なえます。しかし,一つの大きい違いがあります。正確に同じ(大抵,同じものだけ)であるすべての型のheadingを必要とするわけではありません。特に,font-sizeとquadding(揃え〔alignment〕)がheadingごとに変化すべきですが,font-family-nameは変化しないでしょう。そして,line-spacing,line-before,line-after,その他の間隔属性〔spacing attribute〕はすべてのheadingについて固定されるかfont-sizeに基づくかのいずれかです。必要なことは定義を引数化〔to parameterize〕することです。言い換えれば,引数〔parameter / argument〕をとる定義を必要とします。ここに,そのような定義があります。
(define (heading-procedure heading-font-size heading-quadding) (make paragraph font-family-name: "中ゴシック体" font-weight: 'bold font-size: heading-font-size space-before: (* 0.5 heading-font-size) start-indent: 6pt first-line-start-indent: -6pt quadding: heading-quadding keep-with-next?: #t))
見出し語〔keyword〕“define”の直後にある(heading-procedure font-size quadding)が手続き原型〔procedure prototype〕です。それは機能の一組みに対応します。先ず,それはこの手続きが呼ばれる方法を示します。ちょうど式(+ 5 3)において“+”が手続き名であるように,括弧の中の最初のトークンが手続き名〔procedure name〕です。この場合は,heading-procedureです。手続き原型の中の他のトークンは,引数です。この手続きは2個の引数をとります。一つはheading-font-sizeと呼ばれ,もう一つはheading-quaddingと呼ばれます。どんな必要なものについても引数で呼ぶことができます。もちろん,defineとかifのような見出し語を用いることはできませんが,ほとんどの他の名前は使えます。手続きの内側において,まるでdefine文をもって定義されたかのように,heading-font-sizeとheading-quaddingが使えます。それらは変数ですが,局所変数〔local variable〕です。それらはこの手続きの内側にだけ存在します。ここに,heading-procedureを用いる方法があります。
(element h1 (heading-procedure 24pt 'center)) (element h2 (heading-procedure 18pt 'start))
これらは手続き呼出し〔procedure call〕と名付けられています。これがどのように働くかはすでに理解しているかもしれませんね。各々の手続き呼出しについては,二つのカットアンドペースト〔cut-and-paste〕操作を想像してみて下さい。最初に,先に定義されたheading-procedureのコピーを作り,それを手続き呼出しの上へペーストします。次に,定義の中で対応している引数の各々の出現の上へ手続き呼出しからの引数をコピーします。仮にそれが完全に「展開」できるのであれば,それらの手続き呼出しは次のように見えるでしょう。
(element h1 (make paragraph font-family-name: "中ゴシック体" font-weight: 'bold font-size: 24pt space-before: (* 0.5 24pt) start-indent: 6pt first-line-start-indent: -6pt quadding: 'center keep-with-next?: #t)) (element h2 (make paragraph font-family-name: "中ゴシック体" font-weight: 'bold font-size: 18pt space-before: (* 0.5 18pt) start-indent: 6pt first-line-start-indent: -6pt quadding: 'left keep-with-next?: #t))
カットアンドペーストは手続き呼出しについて考える一つの方法ですが,入力(引数)して出力(返却値)を行なうブラックボックスマシン〔black box machine〕としてそれらをみなすこともできます。“+”は数を取り込み,その数の合計を返却するブラックボックスです。heading-procedureは,フォントサイズを表わしている量とquaddingを表しているシンボルを取り入れ,sosofoを返却するブラックボックスです。高校数学では,f(x)=x+5やf(x,y)=x*yのようなユーザー定義関数〔user-defined function〕を作ります。
表現言語は,同じように働きます。同じことを行なう手続きのための別の構文があることに注意して下さい。
(define heading-procedure (lambda (heading-font-size heading-quadding) (make paragraph font-family-name: "中ゴシック体" font-weight: 'bold font-size: heading-font-size line-spacing: 22pt space-before: 15pt space-after: 10pt start-indent: 6pt first-line-start-indent: -6pt quadding: heading-quadding keep-with-next?: #t))
この構文は,手続きの別の特性を強調しています。また,表現言語のコンピューター科学の理論的な背景も強調しています。表現言語のこれらの重要な機能については,この手引の範囲を超えていますが,別の構文を見た場合に認識できるようにそれらを指摘しておきます。
手続きの説明の終わりにあたって,少し触れておきたいことがあります。手続きは,自分自身を呼ぶこともできます(これは再帰〔recursion〕と呼ばれています)。手続きは,手続きと手続きからの返却値に対して引数であることができます(これは高位関数〔higher-order functions〕と呼ばれています)。大きく複雑なスタイルシートを作りたいのであれば,DSSSLのこれらの機能について学ぶべきです。
DSSSL表現言語は,Schemeと呼ばれるプログラミング言語を手本としています。Schemeに関する本を読めば,その内容がほんのわずかの例外をもって表現言語に直接適用できるということが分かるでしょう。次の二冊の本を推薦します。
MIT SchemeやPLT Schemeのような非常に快適な環境で,Schemeプログラミングを練習できます。DSSSL表現言語が基づいているその言語を習得するための優秀なツールを得るために,ウェブを検索して下さい。既存のDSSSLツールでもたらされる恩恵は,式を編集しながら検査できることで,小さな手続きや式を試すために,スタイルシート全体を書いたり試したりする時間を浪費しないで済みます。学習では,これは非常に有用な機能です。
Daniel M. Germánは,DSSSLのより複雑な使用のいくつかを扱っている手引[
http://www.sil.org/sgml/dssslGerman.html]を持っています。
DSSSL標準[
http://www.jclark.com/dsssl]は,複雑で大きい本ですが,それ自身読みやすくできています。指定をもっと学んだり試したりするうちに,より快適なことを見つけられるでしょう。試してみて下さい!
(1998年3月記)