LoginSignup
3
0

More than 1 year has passed since last update.

Pythonを使って擬似的にプログラミング言語を自作する 改良版

Last updated at Posted at 2021-12-16

う え き ぷ に き あ く ん 笑 Advent Calendar 2021 16日目です

環境

macOS Catalina 10.15.7
python 3.9.7
clang 12.0.0

やりたいこと

前回の記事のコードを改良したい(主に計算量最適化など)
ces言語(作成した言語)の機能を少し増やしたい

計算量を解析してみる

最初に、前回の記事で最終的に完成したコードの計算量を簡単に解析してみます。めちゃくちゃ見にくいコードですが、当時のぼくを尊重して(?)一旦コピペをします(コメントアウト部分を少し変更しています)。

ces.py
# コマンド実行・エラー発生時に途中終了するためのモジュールを読み込む
import sys
import os

# 変数の宣言
inp=""
tmp=0
out=""
INPUT=[]
OUTPUT=[]
ERROR=[]
k=0
com=""
bo=False

# トランスパイルの対応表
ces_command=["imp","usi","impio","impbit","out","box","!","#","retu","{","}"]
cpp_command=[["#include <",1,">"],["using namespace ",1,";"],["#include <iostream>\nusing namespace std;"],["#include <bits/stdc++.h>\nusing namespace std;"],["cout << ",1," << endl",";"],[1," ",2," = ",3,";"],[1],["// ",1],["return ",1,";"],["{"],["}"]]

# 引数の取得
fni=sys.argv
fn=fni[1]
cn=fni[2]

# 第1引数で指定されたファイルを読み込み、cesのコードをリストに追記していく
_file=open(fn,"r")
for line in _file:
    inp=line
    if inp[0]=="!":
        INPUT.append(["!",inp[1:len(inp)]])
    else:
        INPUT.append(list(map(str,inp.split())))

# ファイルを閉じる
_file.close()

# 対応表を使ってトランスパイル・エラーの判定
for i in S:
    if i[0] in ces_command:
        tmp = ces_command.index(i[0])
        for u in cpp_command[tmp]:
            if type(u) is int:
                if u<=len(i)-1:
                    out += i[u]
                else:
                    k=0
                    for q in cpp_command[tmp]:
                        if type(q) is int and q > k:
                            k=q
                    ERROR.append([str(i)," ^\nerror!\nThere are not enough arguments\nRequired amount : "+str(k)])
                    bo=True
                    break
            else:
                out += u
        OUTPUT.append([out])
        out = ""
    elif i != ["END"]:
        ERROR.append([i," ^\nerror!\nThere is no ",i," instruction"])
        bo=True

# エラーが出ていなければトランスパイルした結果を第2引数で指定されたファイル.cppに上書きし、トランスパイルの成功を通知する。エラーが出ていればエラーを出力する
if bo == True:

    # エラーの出力を見やすくするために一行空ける
    print()
    for i in E:
        for j in i:
            print(j)
        print()
else:
    print(":) < ces trance was successful!")
    _file = open(cn+".cpp","w")
    for i in O:
        for j in i:
            _file.write(j)
    # ファイルを閉じ、第2引数で指定されたファイル名の実行ファイルを作成する
    _file.close()
    com="g++ "+cn+".cpp -o "+cn
    os.system(com)
    # トランスパイルしてcppファイルは消しておく
    com="rm "+cn+".cpp"
    os.system(com)

なぁ〜にいっちょまえにinなんて使ってるんだか 頭が痛いです
一度コードの流れを書き出してみると次のようになっています。

cesを一行ずつ読み、その行が命令として定義されているかをリストを全探索して調べ、定義されているなら対応するcppのコードを取得する。その後、cppのコード中に引数を必要とする箇所があるかを調べ、あるなら渡す。
cesの命令として定義されていない場合や、必要な引数が用意されていない場合はエラーとする。

このとき、ces言語の行数, 命令の種類数をN, Mとし、cesに対応するcppコードをトランスパイルしやすく区切った量をKとすると、時間計算量はO(N(M+K))となります。

計算量を改善する

cesとcppを対応させるのにdictを使いましょう
おわり おわりです 計算量はO(NK)です

機能を追加する

さすがにこれだけだとやばいので、機能を追加しようと思います

# コマンド実行・エラー発生時に途中終了するためのモジュールを読み込む
import sys
import os

# 変数の宣言
inp=""
tmp=0
out=""
INPUT=[]
OUTPUT=[]
ERROR=[]
k=0
com=""
bo=False
inf_count=0

# トランスパイルの対応表
ces_to_cpp={
    "imp": ["#include <",1,">"], 
    "usi": ["using namespace ",1,";"], 
    "impio": ["#include <iostream>\nusing namespace std;"], 
    "impbit": ["#include <bits/stdc++.h>\nusing namespace std;"], 
    "out": ["cout << ",1," << endl",";"], 
    "box": [1," ",2," = ",3,";"], 
    "!": [1], 
    "#": ["// ",1], 
    "retu": ["return ",1,";"], 
    "{": ["{"], 
    "}": ["}"], 
    "rep{": ["for(int ",1,"=",2,";",1,3,4,";",1,"++){"], 
    "inf": ["while(true){"], 
    "break": ["break;"], 
    "func": [1," ",2,"()","{"]
}

# 引数の取得
fni=sys.argv
if len(fni)!=3:
    print("No input files")
    exit(1)
fn=fni[1]
cn=fni[2]

# 第1引数で指定されたファイルを読み込み、cesのコードをリストに追記していく
_file=open(fn,"r")
for line in _file:
    inp=line
    if inp[0]=="!":
        INPUT.append(["!",inp[1:len(inp)]])
    else:
        INPUT.append(list(map(str,inp.split())))

# ファイルを閉じる
_file.close()

# 対応表を使ってトランスパイル・エラーの判定
for i in INPUT:
    if not i:
        continue
    if i[-1][-2:] == "\n":
        i[-1] = i[-1][:-2]
    if i[0] in ces_to_cpp:
        tmp = ces_to_cpp[i[0]]
        if i[0] == "inf":
            inf_count += 1

        for u in tmp:
            if type(u) is int:
                if u<=len(i)-1:
                    out += i[u]
                else:
                    k=0
                    for q in tmp:
                        if type(q) is int and q > k:
                            k=q
                    ERROR.append([str(i)," ^\nerror!\nThere are not enough arguments\nRequired amount : "+str(k)])
                    bo=True
                    break
            else:
                out += u
        OUTPUT.append([out])
        out = ""
    elif i != ["END"]:
        ERROR.append([i," ^\nerror!\nThere is no ",i," instruction"])
        bo=True
for i in range(inf_count):
    OUTPUT.append(["}"])


# エラーが出ていなければトランスパイルした結果を第2引数で指定されたファイル.cppに上書きし、トランスパイルの成功を通知する。エラーが出ていればエラーを出力する
if bo == True:

    # エラーの出力を見やすくするために一行空ける
    print()
    for i in ERROR:
        for j in i:
            print(j)
        print()
else:
    print(":) < ces trance was successful!")
    _file = open(cn+".cpp","w")
    for i in OUTPUT:
        for j in i:
            _file.write(j)
    # ファイルを閉じ、第2引数で指定されたファイル名の実行ファイルを作成する
    _file.close()
    com="g++ "+cn+".cpp -o "+cn
    os.system(com)
    # トランスパイルしてcppファイルは消しておく
    com="rm "+cn+".cpp"
    os.system(com)

しました(え?)
無限ループ
for文
break
と、うまく動作しない部分を修正しました。
今回試したcesファイルです。

impio
func int main null
box int nyan 5
box int i 0
rep{ i 0 < nyan
out nyan
out i
}
inf
!i++;
out i
!if(i==10){
retu 0
break
}
}

終わりです2

今回の記事を執筆するにあたって思ったことがあります。それは、

保守性はめちゃくちゃ大事

ということです。改良するにあたり、コードを読む時間が本当に無限時間必要でした。いつか保守性についての記事も書いてみたいです。

明日の記事は〜〜〜うえきさん!だよ!!!

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0