まず
筆者はプログラミング初心者、かなり無知マンなので、様々な名称を知らず、"***なやつ"等曖昧な表現が用いられますがご容赦ください。
やることとか
bashライクな書き方で動作するやつ(なんて言うのかしらない)をつくりたい!のでつくりました。
作成したプログラムは、例えば次のようなスクリプトを解析・実行して結果を返します
rand -s3 a b c + say def | replace e E
(結果は例えばb a cdEf
のようになります)
この記事では、具体的な処理は割愛し、簡単にLarkの使い方を解説していこうと思います。
使用しているライブラリ
Lark - a modern parsing library for Python
文法規則ファイルの作成
まずパーサジェネレータに渡す、文法規則を記述したテキストファイルを作成します。
今回の場合は、
script
は1つ以上のchunk
がjoin
で繋がれたもので、
chunk
は1つ以上のsentence
がpipe
で繋がれたもので...
といった具合に記述していきます。
script : [chunk join] chunk
chunk : [(sentence pipe)+] sentence
sentence : command [space option] [(space arg)+] [/\n+/]
command : chars
| ([chars] subshell [chars])+
option : "-" (chars|subshell)+
arg : chars
| "'" allchars "'"
| ([chars] subshell [chars])+
subshell : "(" script ")" [/\n+/]
chars : /[^\+\|\s\(\)']+/[/\n+/]
allchars : /[^']+/[/\n+/]
space : " "
join : [space] "+" [space]
pipe : [space] "|" [space]
パーサの作成
パーサの作成の仕方です。先ほど作成したテキストファイルを読み込んで、Larkに渡すだけです。
from lark import Lark
with open("Grammar.lark",encoding="utf-8") as grammar:
LP = Lark(grammar.read(),start="script")
Lark()
の第2引数start=
には、いちばん外側の構成要素を書いておきます(たぶん)。
このパーサで試しにrand -s3 a b c + say def | replace e E
を解析してみましょう。
解析結果ツリーのpretty()
メソッドで、可読性を高く表示できますので、これを使いましょう。
tree = LP.parse("rand -s3 a b c + say def | replace e E")
print(tree.pretty())
script
chunk
sentence
command
chars rand
space
option
chars s3
space
arg
chars a
space
arg
chars b
space
arg
chars c
join
space
space
chunk
sentence
command
chars say
space
arg
chars def
pipe
space
space
sentence
command
chars replace
space
arg
chars e
space
arg
chars E
(あれ?space
なんかおかしくね?まいっか処理に使わないし...)
トランスフォーマの作成
では、解析したツリーの様子に応じて処理を実行する部分を作っていきましょう。
from lark import Transformer
class mytransformer(Transformer):
def __init__(self): # 適宜使用する変数などを初期化
self.var1 = None
self.var2 = []
def sentence(self,tree): # 第二引数を取るとsentence以深のツリーが使える!
print("sentence") # example
def command(self,tree):
print("command")
def option(self,tree):
print("option")
arg
やchunk
などの処理は省略して記載しました。
mytransformer
内で定義したsentence
やcommand
、option
などは、深度優先で呼び出されます。なので、この例では
command
-> option
-> sentence
といった順に処理が行われます。
トランスフォーマの使い方ですが、
tree = LP.parse("rand -s3 a b c + say def | replace e E")
mytransformer().transform(tree)
のようにして使用します。
最後に
以上、簡単にLarkについて解説させて頂きました。
初心者がどうにかこうにか頑張ったものなので、誤り等あるかと思います。誤りを発見した場合はコメント等いただけると助かります。