
Sean McGrath氏がその著“PARSEME.1ST ; SGML for Software Developers”(Prentice Hall社,1998年)の中で提案しているPython Frameworkは,文字通り,PythonによるSGML文書の処理の枠組みを与えてくれるものです。手短に言うと,SGMLパーザーが出力した線型のESISデータを,SGML文書の本来の構造であるツリー(木)で表わして,それを自由自在に参照できるようにしたものです。また,それはDSSSLでいうグローブ(森)の概念に似ています。
ある文書型定義[図1を参照]に基づく文書実現値[図2を参照]をパーザーで解析すると,ESISデータが得られます。これをEsisBrowserのTREEボタンで見ると,図3のような結果が得られます。これは,線型のESISデータをツリー型へ変換し表現したものです。図3を図4のようにグラフ表現すると,LETTER要素をルート(根)とするツリーが見えてきます。
<!DOCTYPE LETTER [ <!ELEMENT LETTER - - (HEAD,BODY,FROM) -- 手紙 --> <!ELEMENT HEAD O O (TO,DATE,TITLE,ABSTRACT?) -- 頭書き --> <!ELEMENT BODY O O (P)+ +(FN|FNREF) -- 本文 --> <!ELEMENT FN - - (#PCDATA) -- 脚注 --> <!ATTLIST FN ID ID #IMPLIED > <!ELEMENT FNREF - O EMPTY -- 脚注参照 --> <!ATTLIST FNREF ID IDREF #REQUIRED > <!ELEMENT TO - O (#PCDATA) -- 宛先 --> <!ELEMENT DATE - O (#PCDATA) -- 日付 --> <!ELEMENT TITLE - O (#PCDATA) -- 表題 --> <!ELEMENT ABSTRACT - O (#PCDATA) -- 要約 --> <!ELEMENT P - O (#PCDATA) -- 段落 --> <!ELEMENT FROM - O (#PCDATA) -- 発信人 --> ]>
▲図1 文書型定義
<LETTER> <TO>各位 <DATE>1997年10月12日 <TITLE>テキスト処理に関するセミナーのご案内 <P>拝啓 貴社ますますご清栄のこととお慶び申し上げます。 <P>さて,この度,本校では,これからの拡印刷<FN ID="拡印刷">いままでの印刷事業の枠を拡大するという意味で用いています。</FN>の技術教育の一環として,コンピュータ支援<FN ID="コンピュータ支援">パソコン,ワークステーション,専用機を使ったシステムです。</FN>によるテキストの処理(入稿・編集・加工)に関するセミナーを企画致しました。必ずや貴社のお役に立つ内容と存じます。 <P>つきましては,拡印刷<FNREF ID="拡印刷">を目指す貴社に別紙応募要領に基づいてご参加いただくたく,ご案内申し上げます。 <FROM>○△学校 </LETTER>
▲図2 文書実現値
| ELEMENT | ID | TOP | LEFT | RIGHT | BELOW | DATA/ATTRIBUTES |
|---|---|---|---|---|---|---|
| LETTER | 1 | 2 | ||||
| HEAD | 2 | 1 | 6 | 3 | ||
| TO | 3 | 2 | 4 | 14 | ||
| 14 | 3 | 各位 | ||||
| DATE | 4 | 2 | 3 | 5 | 15 | |
| 15 | 4 | 1997年10月12日 | ||||
| TITLE | 5 | 2 | 4 | 16 | ||
| 16 | 5 | テキスト処理に関するセミナーのご案内 | ||||
| BODY | 6 | 1 | 2 | 13 | 7 | |
| P | 7 | 6 | 8 | 17 | ||
| 17 | 7 | 拝啓 貴社ますますご清栄のこととお慶び申し上げます。 | ||||
| P | 8 | 6 | 7 | 11 | 18 | |
| 18 | 8 | 9 | さて,この度,本校では,これからの拡印刷 | |||
| FN | 9 | 8 | 18 | 20 | 19 | {'ID': (0, 'TOKEN', '拡印刷')} |
| 19 | 9 | いままでの印刷事業の枠を拡大するという意味で用いています。 | ||||
| 20 | 8 | 9 | 10 | の技術教育の一環として,コンピュータ支援 | ||
| FN | 10 | 8 | 20 | 22 | 21 | {'ID': (0, 'TOKEN', 'コンピュータ支援')} |
| 21 | 10 | パソコン,ワークステーション,専用機を使ったシステムです。 | ||||
| 22 | 8 | 10 | によるテキストの処理(入稿・編集・加工)に関するセミナーを企画致しました。必ずや貴社のお役に立つ内容と存じます。 | |||
| P | 11 | 6 | 8 | 23 | ||
| 23 | 11 | 12 | つきましては,拡印刷 | |||
| FNREF | 12 | 11 | 23 | 24 | {'ID': (0, 'TOKEN', '拡印刷')} | |
| 24 | 11 | 12 | を目指す貴社に別紙応募要領に基づいてご参加いただくたく,ご案内申し上げます。 | |||
| FROM | 13 | 1 | 6 | 25 | ||
| 25 | 13 | ○△学校 |
▲図3 SGMLTreeの情報
|
▲図4 SGMLTreeのオブジェクト
図4において,LETTER要素は,HEAD要素,BODY要素,FROM要素から構成されています。先ず,LETTER要素から下へ矢印が伸びてHEAD要素を指しています。次に,HEAD要素から右へ矢印が伸びてBODY要素を指し,さらにBODY要素から右へ矢印が伸びてFROM要素を指しています。つまり,この構造は,次の内容モデルを表わしているわけです。
<!ELEMENT LETTER - - (HEAD,BODY,FROM)>
同様に,HEAD要素から下へ伸びる矢印をたどると,次の内容モデルを表わしていることが分かるでしょう。
<!ELEMENT HEAD - - (TO,DATE,TITLE)>
さらに,それぞれの要素から下へ伸びる矢印をたどると,データを指していることが分かります。
他の要素とデータの関係も同様に表わされています。とくに図5のBODY要素では,P要素が繰り返され,途中にFN要素やFNREF要素が混じっていることに注目して下さい。段落(P)の中に脚注(FN)や脚注参照(FNREF)が出現すると,段落のデータはそこで途切れて,FN要素やFNREF要素の右側へ向かってつながるようになります。
▲図5 SGMLTreeのオブジェクト(つづき)
ツリーを構成する要素とデータはノード(節)と呼ばれます。図6のように,要素ノードは要素名と属性を表わすオブジェクトです。データノードはデータの内容を表わすオブジェクトです。内部的な記憶としては,ノードは特定のアドレスに格納され,矢印はポインターと呼ばれる,そのアドレスを指すオブジェクトです。
|
▲図6 SGMLTreeのノード
図7は,一つのノードに四つのポインターがあることを示しています。上を向くポインターは,それ自身が依存しているノードを指しています。最上位のLETTER要素にはそれがありません。下を向くポインターは,それ自身の下にあるノードを指しています。末端のデータノードにはそれがありません。こうして上下の方向を示すポインターによって親子関係が表わされます。ここで,左右のポインターの意味はもう明らかでしょう。それらは兄弟関係を表わすものです。
|
▲図7 SGMLTreeのポインター
こうしたツリー構造の要素とそれに対する操作をPython Frameworkは,SGMLTreeという名前のクラスで定義しています。
クラスとは,インスタンスオブジェクトの型紙としてのオブジェクトで,あるデータの構造とそれに対する操作(つまり,メソッド)を定めたものです。肝心なことは,それだけでは何もしないということです。ちょうど,SGMLにおける文書型定義(これがクラスに相当)と文書実現値(これが正にインスタンスオブジェクトに相当)の関係と同じです。
Pythonでは,クラスを呼び出して変数に代入すると,その実体(インスタンスオブジェクト)が作られます。例えば,次のように書くと,変数TはSGMLTreeクラスから作成されたインスタンスオブジェクトを指す名前になります。
T = SGMLTree()
そして,図8で示すような各種のメソッド(つまり,関数)がTに対して行なえます。例えば,ルート直下の要素名を得たい場合は,次のように書きます。
T.MoveToRoot() T.MoveDown() ElementName = T.GetElementName()
|
MoveToRoot() MoveDown() MoveUp() MoveRight() MoveLeft() GetSGMLNodeDetails() AtData() AtElement(name) GetData() GetElementName() GetAttributes() SetAttributes(attributes) GetAttributeValue(name) InsertBelow(leaf) InsertRight(leaf) HasDown() HasRight() HasUp() HasLeft() PushPosition() PopPosition() |
▲図8 SGMLTreeクラスメソッド
図9は,Python FrameworkとEsis Browserの関係を表わしています。なお,図9は,LOADボタンを押した時に呼び出されるLoadTreeクラスを描いていますが,その他のDUMPボタンやTEXTボタンなどを押した時も,同じ位置にそれぞれ対応するDumpTreeクラスやSimpleTextクラスなどが呼び出されます。
LoadTreeクラスはSGMLAppletクラスのサブクラスであり,それ自身のメソッドに加えて,SGMLAppletクラスのメソッドも継承しています。言い換えると,SGMLAppletクラスはLoadTreeクラスのスーパークラスということになります。そのSGMLAppletクラスはSGMLTreeクラスを呼び出しています。そのSGMLTreeクラスはメソッドを持ち,SGMLNodeクラスのサブクラスである,SGMLElementNodeクラスとSGMLDataNodeクラスを呼び出しています。SGMLNodeクラスは,ツリー構造を形成する要素のための,単なる型紙であり,メソッドを持っていません。
EsisBrowserでLOADボタンが押されると,LoadTreeクラスのインスタンスLoaderが作成されます。そのインスタンスLoaderに制御が移って,ESISデータの入力が行なわれます。次いで,LoaderでSGMLTreeクラスのインスタンスTが作成されます。そしてインスタンスTに制御が移って,ESISデータのSGMLツリー構造への記憶が行なわれます。
|
▲図9 Python FrameworkとEsis Browserの関係
このように,オブジェクト指向で作られたプログラムは,少々回りくどい仕組みになっています。従来のプログラミング言語(例えば,Perl)のサブルーチンのほうが分かりやすく感じるかもしれません。しかし,クラスとインスタンスという関係は,心配するほど難しいものではありません。いわゆる「ソフトウェアの部品化とその再利用」を図るためには,こうした仕組みがどうしても必要なのです。
Python Frameworkを利用する上で,個々のクラスについて必ずしも知っている必要はありません。表1は,EsisBrowserから直接呼び出すクラスと作成されるインスタンスのすべてです。
とくにSGMLAppletクラスが提供するメソッドは,ツリー構造を自動的にたどってくれるので,特定の要素に対する処理を書くだけで,大半の処理ができます。例えば,HEAD要素に対するメソッドは,ツリーにおけるHEAD要素を識別する手続きなしに,次のように書けます。
def HEAD_HANDLER(self, s) : HEAD要素に対する具体的操作
ESISデータを前提としたコンバーターとかフォーマッターといったプログラムを開発する場合は,Python Frameworkを利用すべきです。たぶん,EsisBrowserに相当するメインプログラムと,具体的な処理を行なう,SGMLAppletクラスのサブクラスを作るだけで,目的のアプリケーションが実現できるでしょう。
▼表1 EsisBrowserが呼び出すクラスと作成されるインスタンス
| インスタンス | クラス | スーパークラス | モジュール |
|---|---|---|---|
| Loader | LoadTree | SGMLApplet | loadtree |
| Dumper | DumpTree | SGMLApplet | dumptree |
| Contexts | ElementContexts | SGMLApplet | cntxrep |
| Texts | SimpleText | SGMLApplet | textrep |
| Tree | TreeReport | SGMLApplet | treerep |
(1998年11月記)