Python Larkを使った構文解析でプログラミング言語を作成

2021年8月1日日曜日

Python

t f B! P L

今回の内容

PythonのLarkを使って構文解析し、プログラミングを言語を作成したいと思います。
本当になんかプログラミング言語というよりはまぁあれなんですけど、ただ遊びとして作成したいと思いま~す。

Larkの使い方

まずは、Larkをインストールしてください。
pip install Lark-parser --upgrade
そしてから次にいくつかのファイルが必要です。
Python Larkを使った構文解析でプログラミング言語を作成
最初に、自分のコードであるファイルを「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("こんにちは");
まぁ、改行関係ないんで改行しなくても変わらないですけど。
そして実行結果はこんな感じです。
Python Larkを使った構文解析でプログラミング言語を作成
こんな感じで出力されます。そして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 フゥ
すげぇ説明難しい。
まぁ分からないことは気軽にコメントで!!では。

このブログを検索

要望について

ブログのレイアウトやテーマについての提案をいただきました。現在qooqテーマを適応中です。 語尾を自動でつけるプログラムを作りたいという要望をいただきました。是非紹介したいですがどの言語でどのようなプログラムかなど、お問い合わせフォームで詳しく教えてください!

最近の出来事

最近の出来事
寿司打お勧め75位♪(練習モードだけど)

アンケート

Welcome!

「プログラミング独学ブログ」へようこそ。 Yakinyといいます。幅広い範囲で投稿していますので、ぜひ過去の記事も見てみてください!!コメントも大歓迎です!更新がとまってしまって申し訳ございません

お問い合わせ

名前

メール *

メッセージ *

QooQ