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)"))