19.2. "sgmllib" --- 単純な SGML パーザ
**************************************

バージョン 2.6 で非推奨: "sgmllib" モジュールは Python 3 で削除されま
した。

このモジュールでは SGML (Standard Generalized Mark-up Language: 汎用マ
ークアップ言語標準) で書式化されたテキストファイルを解析するための基礎
として働く "SGMLParser" クラスを定義しています。実際には、このクラスは
完全な SGML パーザを提供しているわけではありません --- このクラスは
HTML で用いられているような SGML だけを解析し、モジュール自体も
"htmllib" モジュールの基礎にするためだけに存在しています。XHTML をサポ
ートし、少し異なったインタフェースを提供しているもう一つの HTML パーザ
は、 "HTMLParser" モジュールで使うことができます。

class sgmllib.SGMLParser

   "SGMLParser" クラスは引数無しでインスタンス化されます。このパーザは
   以下の構成を認識するようにハードコードされています:

   * "<tag attr="value" ...>" と "</tag>" で表されるタグの開始部と終
     了 部。

   * "&#name;" 形式をとる文字の数値参照。

   * "&name;" 形式をとるエンティティ参照。

   * "<!--text-->" 形式をとる SGML コメント。末尾の ">" とその直前に
     あ る "--" の間にはスペース、タブ、改行を入れることができます。

一つの例外が以下のように定義されます:

exception sgmllib.SGMLParseError

   "SGMLParser" クラスで構文解析中にエラーに出逢うとこの例外が発生しま
   す。

   バージョン 2.1 で追加.

"SGMLParser" インスタンスは以下のメソッドを持っています:

SGMLParser.reset()

   インスタンスをリセットします。未処理のデータはすべて失われます。イ
   ンスタンス化の際に暗黙的に呼び出されます。

SGMLParser.setnomoretags()

   タグの処理を停止します。以降の入力をリテラル入力 (CDATA) として扱い
   ます。(この機能は HTML タグ "<PAINTEXT>" を実装できるようにするため
   だけに提供されています)

SGMLParser.setliteral()

   リテラルモード (CDATA モード) に移行します。

SGMLParser.feed(data)

   テキストをパーザに入力します。入力は完全なエレメントから成り立つ場
   合に限り処理されます; 不完全なデータは追加のデータが入力されるか、
   "close()" が呼び出されるまでバッファに蓄積されます。

SGMLParser.close()

   バッファに蓄積されている全てのデータについて、直後に EOF が来た時の
   ようにして強制的に処理します。このメソッドは派生クラスで再定義して
   、入力の終了時に追加の処理を行うよう定義することができますが、この
   メソッドの再定義されたバージョンでは常に "close()" を呼び出さなけれ
   ばなりません。

SGMLParser.get_starttag_text()

   最も最近開かれた開始タグのテキスト部分を返します。このテキストは必
   ずしも元データを構造化する上で必須ではありませんが、 "広く知られて
   いる (as deployed)" HTML を扱ったり、入力を最小限の変更で再生成 (属
   性間の空白をそのままにするなど) したりする場合に便利なことがありま
   す。

SGMLParser.handle_starttag(tag, method, attributes)

   このメソッドは "start_tag()" か "do_tag()" のどちらかのメソッドが定
   義されている開始タグを処理するために呼び出されます。 *tag* 引数はタ
   グの名前で、小文字に変換されています。 *method* 引数は開始タグの意
   味解釈をサポートするために用いられるバインドされたメソッドです。
   *attributes* 引数は "(name, value)" のペアからなるリストで、タグの
   "<>" 括弧内にある属性が収められています。

   *name* は小文字に変換されます。 *value* 内の二重引用符とバックスラ
   ッシュも変換され、と同時に知られている文字参照および知られているエ
   ンティティ参照でセミコロンで終端されているものも変換されます(通常、
   エンティティ参照は任意の非英数文字で終端されてよいのですが、これを
   許すと非常に一般的な "<A HREF="url?spam=1&eggs=2">" 　において
   "eggs" が正当なエンティティ参照であるようなケースを破綻させます)。

   例えば、タグ "<A HREF="http://www.cwi.nl/">" を処理する場合、このメ
   ソッドは "unknown_starttag('a', [('href', 'http://www.cwi.nl/')])"
   として呼び出されます。基底クラスの実装では、単に *method* を単一の
   引数 *attributes* と共に呼び出します。

   バージョン 2.5 で追加: 属性値中のエンティティおよび文字参照の扱い.

SGMLParser.handle_endtag(tag, method)

   このメソッドは "end_tag()" メソッドの定義されている終了タグを処理す
   るために呼び出されます。 *tag* 引数はタグの名前で、小文字に変換され
   ており、 *method* 引数は終了タグの意味解釈をサポートするために使わ
   れるバインドされたメソッドです。 "end_tag()" メソッドが終了エレメン
   トとして定義されていない場合、このハンドラは呼び出されません。基底
   クラスの実装では単に *method* を呼び出します。

SGMLParser.handle_data(data)

   このメソッドは何らかのデータを処理するために呼び出されます。派生ク
   ラスで上書きするためのメソッドです; 基底クラスの実装では何も行いま
   せん。

SGMLParser.handle_charref(ref)

   このメソッドは "&#ref;" 形式の文字参照 (character reference) を処理
   するために呼び出されます。基底クラスの実装は、 "convert_charref()"
   を使って参照を文字列に変換します。もしそのメソッドが文字列を返せば
   "handle_data()" を呼び出します。そうでなければ、エラーを処理するた
   めに "unknown_charref(ref)" が呼び出されます。

   バージョン 2.5 で変更: ハードコードされた変換ではなく
   "convert_charref()" を使うようになりました。

SGMLParser.convert_charref(ref)

   文字参照を文字列に変換するか、 "None" を返します。 *ref* は文字列と
   して渡される参照です。基底クラスでは *ref* は 0--255 の範囲の十進数
   でなければなりません。そしてコードポイントをメソッド
   "convert_codepoint()" を使って変換します。もし *ref* が不正もしくは
   範囲外ならば、 "None" を返します。このメソッドはデフォルト実装の
   "handle_charref()" と属性値パーザから呼び出されます。

   バージョン 2.5 で追加.

SGMLParser.convert_codepoint(codepoint)

   コードポイントを "str" の値に変換します。もしそれが適切ならばエンコ
   ーディングをここで扱うこともできますが、 "sgmllib" の残りの部分はこ
   の問題に関知しません。

   バージョン 2.5 で追加.

SGMLParser.handle_entityref(ref)

   このメソッドは *ref* を一般エンティティ参照として、 "&ref;" 形式の
   エンティティ参照を処理するために呼び出されます。 このメソッドは、
   *ref* を "convert_entityref()" に渡して変換します。 変換結果が返っ
   てきた場合、変換された文字を引数にして "handle_data()" を呼び出しま
   す; そうでない場合、 "unknown_entityref(ref)" を呼び出します。 標準
   では "entitydefs" は "&amp;" 、 "&apos;" 、 "&gt;" 、 "&lt;" 、およ
   び "&quot;" の変換を定義しています。

   バージョン 2.5 で変更: ハードコードされた変換ではなく
   "convert_entityref()" を使うようになりました。

SGMLParser.convert_entityref(ref)

   名前付きエンティティ参照を "str" の値に変換するか、または "None" を
   返します。変換結果は再パーズしません。 *ref* はエンティティの名前部
   分だけです。デフォルトの実装ではインスタンス(またはクラス)変数の
   "entitydefs" というエンティティ名から対応する文字列へのマッピングか
   ら *ref* を探します。もし *ref* に対応する文字列が見つからなければ
   メソッドは "None" を返します。このメソッドは "handle_entityref()"
   のデフォルト実装からおよび属性値パーザから呼び出されます。

   バージョン 2.5 で追加.

SGMLParser.handle_comment(comment)

   このメソッドはコメントに遭遇した場合に呼び出されます。 *comment* 引
   数は文字列で、 "<!--" と``-->`` デリミタ間の、デリミタ自体を除いた
   テキストが収められています。例えば、コメント "<!--text-->" があると
   、このメソッドは引数 "'text'" で呼び出されます。基底クラスの実装で
   は何も行いません。

SGMLParser.handle_decl(data)

   パーザが SGML 宣言を読み出した際に呼び出されるメソッドです。実際に
   は、 "DOCTYPE" は HTML だけに見られる宣言ですが、パーザは宣言間の相
   違 (や誤った宣言) を判別しません。 "DOCTYPE" の内部サブセット宣言は
   サポートされていません。 *data* パラメタは "<!"...">" 記述内の宣言
   内容全体になります。基底クラスの実装では何も行いません。

SGMLParser.report_unbalanced(tag)

   このメソッドは対応する開始エレメントのない終了タグが発見された時に
   呼び出されます。

SGMLParser.unknown_starttag(tag, attributes)

   未知の開始タグを処理するために呼び出されるメソッドです。派生クラス
   で上書きするためのメソッドです; 基底クラスの実装では何も行いません
   。

SGMLParser.unknown_endtag(tag)

   未知の終了タグを処理するために呼び出されるメソッドです。派生クラス
   で上書きするためのメソッドです; 基底クラスの実装では何も行いません
   。

SGMLParser.unknown_charref(ref)

   このメソッドは解決不能な文字参照数値を処理するために呼び出されます
   。標準で何が処理可能かは "handle_charref()" を参照してください。派
   生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行
   いません。

SGMLParser.unknown_entityref(ref)

   未知のエンティティ参照を処理するために呼び出されるメソッドです。派
   生クラスで上書きするためのメソッドです; 基底クラスの実装では何も行
   いません。

上に挙げたメソッドを上書きしたり拡張したりするのとは別に、派生クラスで
は以下の形式のメソッドを定義して、特定のタグを処理することもできます。
入力ストリーム中のタグ名は大小文字の区別に依存しません; メソッド名中の
*tag* は小文字でなければなりません:

SGMLParser.start_tag(attributes)

   このメソッドは開始タグ *tag* を処理するために呼び出されます。
   "do_tag()" よりも高い優先順位があります。 *attributes* 引数は上の
   "handle_starttag()" で記述されているのと同じ意味です。

SGMLParser.do_tag(attributes)

   このメソッドは "start_tag()" メソッドが定義されていない開始タグ
   *tag* を処理するために呼び出されます。 *attributes* 引数は上の
   "handle_starttag()" で記述されているのと同じ意味です。

SGMLParser.end_tag()

   このメソッドは終了タグ *tag* を処理するために呼び出されます。

パーザは開始されたエレメントのうち、終了タグがまだ見つかっていないもの
のスタックを維持しているので注意してください。 "start_tag()" で処理さ
れたタグだけがスタックにプッシュされます。それらのタグに対する
"end_tag()" メソッドの定義はオプションです。 "do_tag()" や
"unknown_tag()" で処理されるタグについては、 "end_tag()" を定義しては
いけません; 定義されていても使われることはありません。あるタグに対して
"start_tag()" および "do_tag()" メソッドの両方が存在する場合、
"start_tag()" が優先されます。
