pyparsing触ってみた
2020-11-24
こんにちは
こんにちは、僕です。
最近、pyparsingというPythonのライブラリを使用していて、面白いなあと思ったので記事にしてみました。
元々自分は言語解析などに興味があって(NLPとか)、今回は形式言語解析になりますがまとめたいと思います。
pyparsingとは?
これです。
ドキュメントThe pyparsing module is an alternative approach to creating and executing simple grammars, vs. the traditional lex/yacc approach, or the use of regular expressions. The pyparsing module provides a library of classes that client code uses to construct the grammar directly in Python code.
だそうです。英語わかんねえや。(シンプルな文章をいい感じにチャチャっとするためのモジュールだお!レキサーとかとはちゃうで、クライアントから直接やねんな。みたいなこと言ってると思います知らんけど)
要は文章をいい感じに分析する補助をしてくれます。
自分は今回プログラミング言語を作るときの補助として使用しました(本記事では触れませんが)
ジャブ
まずは簡単にパーサを作ってみようと思います。
from pyparsing import alphas, Word, Literal, nums
integer_literal = Literal("vat") + Word(alphas) + Literal("=") + Word(nums)
print(integer_literal.parseString('vat i = 10'))
>>> ['vat', 'i', '=', '10']
こんな感じでvarで変数定義、次に変数名、次に代入、次に実際に格納する値(ここではまだ格納してない)を構文解析することができました。
ちょっと実践
実践とか言ってるけど初歩中の初歩です。
if文を解析するプログラムを書いてみたいと思います。
from pyparsing import *
ident = Literal( 'IDENT')
const = Literal( 'CONST')
assign = Literal( '=' )
lparen = Literal( '(' )
rparen = Literal( ')' )
op_arith = oneOf( '+ - * / ** %' )
op_logic = oneOf( '== != <= < >= > && || !')
op_bits = oneOf( ' & | ^ ~ << >>' )
operator = op_arith ^ op_logic ^ op_bits
factor = ( Optional(lparen) + Optional( operator ) + Optional(lparen) + ( ident ^ const ) + Optional( rparen ) )
term = factor + ZeroOrMore( operator + factor )
expr = term + ZeroOrMore( operator + term )
rsv_if = Literal( 'if')
rsv_elif = Literal( 'elif')
rsv_else = Literal( 'else')
_if = rsv_if + expr + '{' + SkipTo( '}' )
_elif = rsv_elif + expr + '{' + SkipTo( '}' )
_else = rsv_else + '{' + SkipTo( '}' )
if_statement = _if.setResultsName('IF')
elif_statement = _elif.setResultsName('ELIF')
else_statement = _else.setResultsName('ELSE')
statement = (if_statement ^ elif_statement ^ else_statement)
#statement
test='''\
if CONST > CONST { IDENT = COST }
if CONST == IDENT { IDENT = CONST EOS IDENT = IDENT EOS }
if CONST != IDENT { IDENT = CONST } else { IDENT = CONST + IDENT }
'''
r = statement
for i in r.scanString(test):
print('{}'.format(' '.join([s for s in i[0]])))
ちょっと色々いきなり新しいものがたくさん出てきてしまいましたが、全てpyparsingの中身にあるメソッドしか使ってないので心配は要りません!
文法はPythonチックになっています。
それぞれのpyparsingのメソッドについて説明したいと思います。
どうでしょう!この上記の物だけでif文を構文解析できます!
また、それぞれのメソッドやクラスの説明をドキュメントで見て、簡単な言語解析の知識があればPyparsingを利用して簡単にプログラムの構文解析をすることができます。
自分がGoで作るインタプリタでコードを書いていたときはリテラルやステートメントは自分で定義しないといけなかったためとても大変でした(と言ってもグローバルな変数として格納して都度呼び出すってだけだったけど)
こうやって動的に簡単にリテラルやステートを定義できるというのはとても嬉しいことだと感じています。
まとめ
あまり内容の深い記事ではありませんでしたが、楽に構文解析をすることができることがわかったと思います。
また、これからPyparsingを使用して様々な解析を行えるという手応えを掴むことができたので良かったです。
形式言語以外でもいけそう???( ̄▽ ̄)