青空文庫形式のルビをhtmlのrubyタグに変換するモジュール

一体これが何番煎じになるのか想像もつかないが、必要になったので書いた。
青空文庫形式の文法は

ルビのかかる部分を明示する

文章に|ルビ《ruby》を打つ。

明示しない

文章にルビ《ruby》を打つ。

という2パターンで、
後者は文字種の境界までがルビのかかる範囲になるという仕様。
上の例は両者とも正しい範囲にかかる。


シンプルに

"""("""
    """(|([^《]{1,50}))"""
    """|"""
    """("""
        """[0-9]{1,50}|"""
        """[a-zA-Z]{1,50}|"""
        u"""[ぁ-ん]{1,50}|"""
        u"""[ァ-ン]{1,50}|"""
        u"""[一-龠]{1,50}"""
    """)"""
""")"""
"""《"""
    """([^》]{1,50})"""
"""》"""
html = self.pattern.sub('<ruby><rb>\\3\\4</rb><rp>(</rp><rt>\\5</rt><rp>)</rp></ruby>', str)

でうまくいくだろうと思ったら、
後方参照の\3と\4は、どちらかが必ずマッチしないため、エラーになる。
以下の問題。
Python Regexp 2.7
https://launchpad.net/~pythonregexp2.7
Unmatched Group issue - workaround
http://bugs.python.org/issue1519638
ここで後方参照\1を取得しても、|が入ってしまうので綺麗に一発で置換できない。


これをとりあえずスクリプト側で回避しようと拡張正規表現を眺めてると

(?(id/name)yes-pattern|no-pattern)
    グループに id が与えられている、もしくは name があるとき、yes-pattern とマッチします。
    存在しないときには no-pattern とマッチします。 

という打ってつけのものがあったので、

"""(|)?"""
"""("""
    """(?(1)"""
        """([^《]{1,50})"""
        """|"""
        """("""
            """[0-9]{1,50}|"""
            """[a-zA-Z]{1,50}|"""
            u"""[ぁ-ん]{1,50}|"""
            u"""[ァ-ン]{1,50}|"""
            u"""[一-龠]{1,50}"""
        """)"""
    """)"""
""")"""
"""《"""
    """([^》]{1,50})"""
"""》"""
html = self.pattern.sub('<ruby><rb>\\2<rb></rb><rp>(</rp><rt>\\5<rt></rt><rp>)</rp></ruby>', str)

とした。
|にマッチするかどうかで処理を分けると、|を追い出せる、ということ。
拡張正規表現はなかなか楽しそうだ。が、拡張正規表現の括弧は後方参照で使える括弧ではないという点には注意が必要。当然の話だがちょっとハマった…


ついでに新潮社形式

文章に#ルビ{ruby}を打つ。

も加えてモジュールにしておいた。
新潮社形式の方が入力しやすいと思うが、blessはあり得てしまいそうなので、青空文庫形式の方が誤った変換の危険がきっと少ない。

>>> from rubytext.aozora import Aozora
>>> aozora = Aozora()
>>> aozora.convert_to_html(u'文章に|ルビ《ruby》を打つ。')
文章に<ruby><rb>ルビ</rb><rp>(</rp><rt>ruby</rt><rp>)</rp></ruby>を打つ。


rubytext
http://bitbucket.org/rubyu/rubytext/