サブパッケージ docutils.parsers¶
Docutils の構文解析サブパッケージ docutils.parsers について記す。構成はクラス Parser およびそのサブクラスの定義と、サブサブパッケージ
docutils.parsers.rst の二本立てになっている。本節では少なくても前者はしっかりと読み込んでいきたい。余力があれば後者も RSTStateMachine という面白い教材があるので解読したい。
クラス図¶
クラス Parser 周辺のクラス図を以下に示す。ただし、説明済みのクラスは省略したり灰色に塗りつぶしたりしてある。
クラス
Parserの直接スーパークラスはComponentだが省略してある。 Docutils の基本クラスについては 基礎クラス群 参照。状態機械系の抽象クラス群については モジュール docutils.statemachine 参照。
クラス Parser¶
クラス Parser は構文解析を実行するためにあるわけだが、ろくなものが実装されていない。
データ¶
クラスデータは右辺値としてアクセスする。
component_type = 'parser'スーパークラス
Componentからの上書き。config_section = 'parsers'スーパークラス
SettingsSpecからの上書き。
メンバーデータは二つだけある。ただしこれらのデータは setup_parse を呼び出さないとアクセスできない。
inputstring文字列データ。基本的には入力テキストデータそのもので、テキスト解析の直前にこれを加工して状態機械の入力とする。
documentクラス
documentのオブジェクト。初期状態でセットする。クラス docutils.nodes.Node も参照。
メソッド¶
メソッドは次の三つしかないのだが、全てクライアント側が直接呼び出すことになるようだ。
parse(self, inputstring, document)文字列を受け取って
documentオブジェクトを肉付けしていくためのメソッドだろうが、これは完全にオーバーライド専用になっている。デフォルトではNotImplementedErrorを送出するだけだ。setup_parse(self, inputstring, document)オブジェクトのメンバーデータをセットするだけのメソッド。これはサブクラスで上書きしないでよい。
finish_parse(self)テキストの構文解析を終了したときに呼び出すメソッド。これはサブクラスで上書きしないでよい。
setup_parseとfinish_parseはサブクラスのparseから呼び出すか、Parserオブジェクトのクライアントがparse呼び出しの前後に呼び出す。
サブクラス¶
モジュール null とサブパッケージ rst にそれぞれ定義されている。とある理由により、クラス名はすべて同じく Parser だ。
クラス docutils.parsers.null.Parser¶
これはいわゆる Null Object パターンのクラスだ。メソッド parse を何もしないように上書きしたものになっている。
ただしクラスデータ
supported,config_section, etc. が意味のあるデータが上書きされているのが惜しい。このクラスのオブジェクトを生成するには、クライアントがモジュール
docutils.parsers.nullからこのクラスを直接 import してコンストラクターを明示的に呼び出す必要がある。
クラス docutils.parsers.rst.Parser¶
これは reStructuredText データを構文解析するクラスだ。しかし処理を
RSTStateMachine と RSTState 系に委譲している。次にメソッド parse のコードを一部改変引用する。
from docutils.statemachine import string2lines
from .states import RSTStateMachine
def parse(self, inputstring, document):
"""Parse `inputstring` and populate `document`, a document tree."""
self.setup_parse(inputstring, document)
self.statemachine = RSTStateMachine(...)
inputlines = string2lines(inputstring, ...)
self.statemachine.run(inputlines, document, ...)
self.finish_parse()
利用例¶
クラス Parser の何らかのサブクラスのオブジェクトを利用する擬似コードを次に示す。
from docutils.utils import new_document
from docutils.parsers import get_parser_class
source_path = '/path/to/source-file'
settings = ... or None
with open(source_path, 'r') as source:
input_string = source.read()
parser = get_parser_class('parser_name')()
parser.parse(input_string, new_document(source_path, settings))
doc = parser.document
...
オブジェクト
settingsについては現在未調査。基本的にはテキストファイルから全文を読み込んでおく。
ここでは関数
get_parser_classを利用してParserのサブクラスを実行時に得ている。それからコンストラクターを直接呼び出す方法でオブジェクトparserを生成する。空の
documentオブジェクトを関数new_documentで生成する。これをparserに渡す。
これらの処理は Docutils の設計では Reader 系が行う想定になっているようだ。
感想¶
Parserのメソッドの設計がやや気に入らない。setup_parseとfinish_parseの呼び出しはスーパークラスのparseに組み込んでおいて、中間部分を上書きさせる Template Method パターンがよかった。関数
get_parser_classで採用している技法は、モジュール サブパッケージ docutils.languages でも見られる。Docutils としては構文解析クラスは例えば
RSTParserのようなものだけで十分事足りるはずだが、敢えて抽象クラスと具象クラスの二段構えに設計しておくことで、別の構文のテキストデータのための構文解析ライブラリーを新しく構築する機会を与えるのだ。これはRSTStateMachineとRSTStateについても当てはまる。RSTStateMachineおよびRSTStateをどこで議論しようか。