今回の内容
PythonのLarkを使って構文解析し、プログラミングを言語を作成したいと思います。本当になんかプログラミング言語というよりはまぁあれなんですけど、ただ遊びとして作成したいと思いま~す。
Larkの使い方
まずは、Larkをインストールしてください。pip install Lark-parser --upgradeそしてから次にいくつかのファイルが必要です。
最初に、自分のコードであるファイルを「code.aiueo」を作成します。ファイル名は何でもいいです。
これをtest.pyにドロップ & ドラッグで実行する感じになります。
そして「test.lark」です。これは構文解析のメインのファイルです。test.pyのなかから呼び出して使います。
test.larkの中身について
prog :[(expr)+]
expr : func [ "(" string ")" ] ";"
string : ESCAPED_STRING
func : /[a-zA-Z]+/
_END : "\n"
%import common.ESCAPED_STRING
%import common.WS
%ignore WS
%ignore _END
このように作られています。どゆこと? って最初はなりますよね、自分も理解するのに丸一日かかりました。され一行目を見てみましょう。
prog :[(expr)+]まず、字句解析、構文解析の最初にprogを行うと指定します。この場合、progが動きます。[(expr)]ってどゆことよ????ってなりますよね。次の行をご覧ください。
expr : func [ "(" string ")" ] ";"
exprというものの中身をもう少し分かりやすくすると。expr は 関数名 ( 文字列 ) ;となります。つまり、関数名(文字列)というふうにプログラムを書けば、このLarkが解析してくれるということです!
ん?じゃぁ一行目いらなくね?ってなりますが、一行目がないと、二回や三回、四回などと、プログラムを往復する(?)ことができなくなります。
まぁ、つまり一回やって終了となってしまうわけです。それで一行目を追加する必要があるのです。
さて三行目
string : ESCAPED_STRINGただの定義。文字列は、インポートしたあれだよ と解析機に命令しています。
func : /[a-zA-Z]+/ただの定義。関数名にはアルファベットが含まれる と解析機に命令しています。
そして行
_END : "\n"改行とは何かを指定しています。アンダーバーを使わなとダメとかかれたサイトがあったので念のためアンダーバーをつけました。
これは何に使うかというと。
とばしてこの行
%ignore _ENDこれが大切です。これは改行を無視しろといっています。つまりPythonのように改行判断しているというよりかは、「;」で終わりを宣言しているような
C言語やC#に近いかんかくです。
ふぅ~~
Python のコード
#!/usr/bin/env python3
import sys
from lark import Lark
from lark import Transformer
from functools import reduce
# 構文解析器の実行部
class CalcTransformer(Transformer):
def prog(self,tree):
return tree
def expr(self, tree):
return reduce(lambda x, y:[x,y] ,tree)
def string (self,tree):
return reduce(lambda x, y:str(reduce(y)) ,tree)
def func (self,tree):
return reduce(lambda x, y:str(reduce(y)) ,tree)
args = sys.argv
if len(args) == 2:
file = open(args[1],"r",encoding="UTF-8")
progr = file.read()
file.close()
with open("test.lark","r") as larker:
parser = Lark(larker.read(),start="prog")
tree = parser.parse(progr)
result = CalcTransformer().transform(tree)
for i in range(len(result)):
if result[i][0] == "Print":
print(result[i][1].replace("\\n","\n")[1:-1],end="")
else:
print("指定された関数はありません。\n")
print(result)
あぁぁぁぁぁまず最初にしていることは、sysをつかってコマンドライン引数を取得し、指定されたファイルを開いています。
んで、test.larkを読んで、Larkにいろいろと仕事をさせます。
さて。resultになにがあるかが大切ですが、まずは以下のコードで実行させたとしましょう。
Print("Hello\n");
Print("こんにちは");
まぁ、改行関係ないんで改行しなくても変わらないですけど。そして実行結果はこんな感じです。
こんな感じで出力されます。そしてresultという変数がどうなっているかが問題ですよ。中身はこんな感じ!!
[[Token('__ANON_1', 'Print'), Token('ESCAPED_STRING', '"Hello\\n"')], [Token('__ANON_1', 'Print'), Token('ESCAPED_STRING', '"こんにちは"')]]
ははw 何だこれ。 for i in range(・・・) をしているので一回目のループをして result[i]の中身はこんなかんじです。[Token('__ANON_1', 'Print'), Token('ESCAPED_STRING', '"Hello\\n"')]
まだよくわからない。ですがこれリストとして使用できるのでresult[i][0]にしたら関数名が返ってくることは分かった。([1]だと文字列)ただし、result[i][1]で返ってくるのはこれです。
"Hello\n"まず、改行されていません。なのでreplaceで"\\n"を"\n"にしています。(つまり、改行コードを改行にしています。 日本語難しい。)
そしてそれだけでは"Hello
"
となります。なので[1:-1]で先頭の文字と後ろの文字を除いて、こうします。
( ´Д`)=3 フゥ
すげぇ説明難しい。
まぁ分からないことは気軽にコメントで!!では。


0 件のコメント:
コメントを投稿