う え き ぷ に き あ く ん 笑 Advent Calendar 2021 16日目です
##環境##
macOS Catalina 10.15.7
python 3.9.7
clang 12.0.0
##やりたいこと##
前回の記事のコードを改良したい(主に計算量最適化など)
ces言語(作成した言語)の機能を少し増やしたい
##計算量を解析してみる##
最初に、前回の記事で最終的に完成したコードの計算量を簡単に解析してみます。めちゃくちゃ見にくいコードですが、当時のぼくを尊重して(?)一旦コピペをします(コメントアウト部分を少し変更しています)。
# コマンド実行・エラー発生時に途中終了するためのモジュールを読み込む
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
今回の記事を執筆するにあたって思ったことがあります。それは、
###保守性はめちゃくちゃ大事
ということです。改良するにあたり、コードを読む時間が本当に無限時間必要でした。いつか保守性についての記事も書いてみたいです。
明日の記事は〜〜〜うえきさん!だよ!!!