pyPEG はへろわるど以前

ぐ…。

同じく Python Parsing Tools より。

最近は PEG が流行してるようなので、「悪いものであるはずがない」と根拠もなく信じ切っていて、あまりにも読みにくくて伝わりづらい公式ドキュメントを頑張って読もう、と格闘したのだが…。

サンプルにあまりに欠ける時点で怯んでしまうわけなんだけれど、「少ないながらも楽しい我が家あるにはあるサンプル」を書き換えて試していたのだが、一言で…「使いにくい」。

の前に、使うのに不可欠なことが一つ:

1 from __future__ import unicode_literals

Python2 では必ずこれを入れなければならない。これはドキュメントにも書かれている。入れないで動かそうとした場合にわかりやすくエラーになってくれるわけではない。

半日ほど格闘してみてわかってきたんだけれど、今今の時点ではどうにも「少しでも外れたことをしようとすると途端に膨大な内部理解とコーディングを要する」ものになっちゃってる。らしい。

具体的に言えば「数値」という定義が簡単に出来ない。さらには「word」の変種を簡単に作れない。そしてこねくりまわそうとすると、「どの誰のどれが何を前提としているのか」のプロトコルがさっぱりわからず、AttributeError やら TypeError を喰らいまくる。ということはあれか、これって「絶賛作りかけ」じゃねーの?? てのがまさしく第一印象。なんというか、「内部をガッツリ理解できたらようやくへろわろでる」というシロモノに思える。嘘だと思うならまさしく「数値」定義をしようとしてみればいい。初学者は果てることウケアイ。以下は「うまく動くやつ」で、これに数値の定義を追加しようと試みてみ:

 1 # -*- coding: utf-8 -*-
 2 from __future__ import unicode_literals
 3 #from __future__ import print_function
 4 from pypeg2 import *
 5 
 6 class Type(Keyword):
 7     grammar = Enum( K("int"), K("long") )
 8 
 9 class Parameter(object):
10     grammar = attr("typing", Type), blank, name()
11 
12 class Parameters(Namespace):
13     grammar = optional(csl(Parameter))
14 
15 class Instruction(str):
16     def heading(self, parser):
17         return "/* on level " + str(parser.indention_level) + " */", endl
18 
19     grammar = heading, word, ";", endl
20 
21 block = "{", endl, maybe_some(indent(Instruction)), "}", endl
22 
23 class Function(List):
24     grammar = attr("typing", Type), blank, name(), "(", attr("parms", Parameters), ")", endl, block
25 
26 #
27 res = parse("int f(int a, long b) { do_this; do_that; }", Function)
28 print(res)
29 #print(dir(res))
30 print(res.name, res.parms)
31 ##print(dir(res.params))
32 ##print(res.params.items())
33 ##print([dir(v) for v in res.params.values()])
34 ##print([dir(v.name) for v in res.params.values()])
35 
36 from pypeg2.xmlast import thing2xml
37 print(thing2xml(res, pretty=True))

よーわからんのだけど、失敗で喰らったエラーを鑑みるに、「python attribute へのマッピング」というアプローチが「完遂してない」ようにも思うし、これは「pyPEG の内部理解をしていないオレサマが悪い」のだけれど(んなわけあるかい)、たとえばここ、バグってる:

pypeg2/__init__.py (208行目から)
208 class Literal(object):
209     """Literal value."""
210     _basic_types = (bool, int, float, complex, str, bytes, bytearray, list,
211             tuple, slice, set, frozenset, dict)
212     def __init__(self, value, **kwargs):
213         if isinstance(self, Literal._basic_types):
214             pass
215         else:
216             self.value = value
217         for k, v in kwargs:
218             setattr(self, k, v)

(多分 python2.7 以前でしか動かないと思う。for k, v in kwargs.items() としないと。)

実は「これ(パーサジェネレータ)の先にあるもの」の方にこの著差の興味がある、というのは明白で、これの著者 Volker Birk がやりたかったのは YML らしく、どうも元はと言えばこれの用が足せればいい、として pyPEG を作ったらしい。だから「テンプレートエンジンの具」としての用途が「ウリ」てことのようなのね。こんな紹介文を見つけた:

The syntax for a PyPEG is on the verbose side, frankly it is too verbose to be productive if you just want to use it for simple parsing. But it is a cool library if you want to parse and manipulate some document in a specific format. For instance you could use it to transform documentation in one format to another.

これは「Composing text」のことを言っている。ここだけ考えたらよいものに思えるし、実際良いものなんだと思うけれどねぇ。

そういうわけで、「将来性はあるのかもしらん」とは思ってはいるんだけれど、少なくとも「今デショ」なシロモノではないと思っている。それよりも何よりもね、実は一番個人的に「やだなぁ」と思っているのは、PEG と言っている癖に、少なくとも利用者はちっとも PEG 「で」書いている気分になれないんだよね。例えば:

1 Ordered choice: e1 / e2

これは「規則 e1 がマッチしなければ e2」という規則を意味するんだけれど、これに別に「/」という演算子が用意されているわけでもないし、「choice」という関数なりがいるわけでもなく、「(構文記述としての) PEG と pyPEG ライブラリ機能のマッピング」が全然。だから書いたプログラムは全然 PEG に見えない。

ところで Python Parsing Tools に列挙されてないものが結構 Parsing In Python: Tools And Libraries に挙がってるので、こちらも見とこーっと。