Lark (A modern parsing library for Python) のへろわるど

Parsing In Python: Tools And Libraries より。

Lark。これは PEG ではなく、Earley と LALR(1)。デフォルトが Earley なんですと。

Parsing In Python: Tools And Libraries ではちょっとピンと来なかったが、Examples#tiny-calculator で一撃で理解出来た。ゆえに一瞬にしてへろわろど:

 1 # -*- coding: utf-8 -*-
 2 import lark
 3 from lark import Lark, InlineTransformer
 4 
 5 parser = Lark('''?sum: product
 6                      | sum "+" product   -> add
 7                      | sum "-" product   -> sub
 8 
 9                  ?product: item
10                      | product "*" item  -> mul
11                      | product "/" item  -> div
12 
13                  ?item: NUMBER           -> number
14                       | "-" item         -> neg
15                       | "(" sum ")"
16 
17                  %import common.NUMBER
18                  %import common.WS
19                  %ignore WS
20          ''', start='sum')
21 
22 class CalculateTree(InlineTransformer):
23     from operator import add, sub, mul, truediv as div, neg
24     number = float
25 
26 def calc(expr):
27     return CalculateTree().transform(parser.parse(expr))
28 
29 print(calc("2 + 3 * (2 * 4 - 5 / 2)"))

クエッションマークとパーセント記号、そして右矢印が lark 固有。クエッションマークは「tells Lark to inline single values」て意味らしい。パーセント記号、というよりか「%import」は言われなくても意味はわかるであろう、「%import」などパーセント記号で始まる部分は lark に対するオプション。そして右矢印は、「OR」の各々の選択ごとにアクションを分岐出来るつー、すばらしい機能だわね。

CalculateTree の意味が取りにくいようであれば以下だと脳内変換すべし:

1 import operator
2 
3 class CalculateTree(InlineTransformer):
4     add = operator.add
5     # ... 以下同じように
6 
7     number = float

うん、すばらしいね!

…といいたかったのだが…。

なんか本日時点のリリース、壊れてない? 今インストールしていざ動かそうとしたら、

1 Traceback (most recent call last):
2   File "lark1.py", line 3, in <module>
3     from lark import Lark, InlineTransformer
4 ImportError: cannot import name Lark

だと。なんだと思って site-packages にインストールされた lark フォルダを見てみると…:

空じゃないか、本体が。

それとなんだか色んな依存をしてて、「redis/」が見えてるのからわかる通りで、redis に依存している。なんだ、んなもんなんに使うんだ??

つーわけで、「ドキュメントから見た印象はすんげー良かった」のに、困ったことに一切評価が始められない。なんだかなぁ…。(今だけだろうと思うので、しばらく経ったら試してみようと思う。そのときは多分「そうだよね、やっぱ素敵」になるんだろうなと想像している。Earley ではなく LALR(1) に切り替えるのも試したかったんだよねぇ…。)


と思ったら。。。

インストールするもん間違えたようで:

1 [me@host: ~]$ pip install lark-parser
2 [me@host: ~]$ # or "py -2 -m pip install lark-parser"
3 [me@host: ~]$ #    "py -3 -m pip install lark-parser"

「lark-parser」でないほうの「lark」にいっしょくたに入ってしまったが、これは正しいんだろうか? 名前衝突してるてこたぁない?

ともあれ上の例は期待通り動作した。

LALR へ乗り換える方法はドキュメントでは見つけられなかったがソース見てすぐに理解出来た:

 1 # -*- coding: utf-8 -*-
 2 import lark
 3 from lark import Lark, InlineTransformer
 4 
 5 parser = Lark('''?sum: product
 6                      | sum "+" product   -> add
 7                      | sum "-" product   -> sub
 8 
 9                  ?product: item
10                      | product "*" item  -> mul
11                      | product "/" item  -> div
12 
13                  ?item: NUMBER           -> number
14                       | "-" item         -> neg
15                       | "(" sum ")"
16 
17                  %import common.NUMBER
18                  %import common.WS
19                  %ignore WS
20          ''', start='sum', parser="lalr")
21 
22 class CalculateTree(InlineTransformer):
23     from operator import add, sub, mul, truediv as div, neg
24     number = float
25 
26 def calc(expr):
27     return CalculateTree().transform(parser.parse(expr))
28 
29 print(calc("2 + 3 * (2 * 4 - 5 / 2)"))