2022.05.09
2022.05.09
Fabeee社員ブログ
目次
初めに
こんにちは。すぎちゃんです。
これまでいくつかの「Pythonで〇〇してみた」シリーズをブログに書いてきましたが、正直ネタ切れです笑
今回も悩みに悩んで、苦肉の策で絞り出したのが「プログラミング言語つくってみた」です。
誤解の無いように先に伝えておくと、言いすぎています。 実際はプログラミング言語なんて一晩では作れませんので、今回は疑似的にそれっぽいものを作ること許していただければと思います。
また、これを参考にもっと良い実装や、より多くのことが出来るように魔改造なんかしてもら えると嬉しいです。
処理の流れを考える
プログラミング言語の実行環境には、コンパイラやインタープリターなどがありますが、今回実装するのは「なんちゃってコンパイラー」です。
- ソースコードの前処理
- ソースコードの関数を解析する
- 関数を実行する
という流れを実装していきます。 また、今回利用するソースコードは
関数 挨拶() -> なし { 表示('こんにちわ');
}
関数 実行() -> なし { 挨拶();
}
こちらを利用してみます。
※ 実行関数は必ず実装するものとし、こちらを実行することを前提条件とします。
ソースコードの前処理
今回ソースコードの前処理は改行コードの削除のみとしています。
def preprocessing(self, text):
"""ソース文字列の前処理
"""
text = text.replace('\n', '')
return text
ソースコードの関数を解析
ソースコードに定義された関数を検索して、整理します。
関数の持つ要素は 関数名・引数・戻り値・内容 の4つとします。
こちらの解析方法はいくつかありますが、今回は簡易的に正規表現を利用してみることとします。
def analysis_func(self, source):
""" 関数構文の検索
"""
funcs = {}
match = re.findall('.+?\s{.+?}', source)
for f in match:
temp = re.match('関数\s(.+)\((.*?)\)\s->(.+){(.+)}', f)
if temp and len(temp.groups()) > 0:
el = temp.groups()
funcs[el[0]] = {
'args': el[1],
'return': el[2],
'content': el[3].strip().split(';'),
}
return func
ソースコードを正規表現で解析し、関数名をキーとした辞書型の変数を返却する処理を行っています。
関数を実行
この言語は 実行 という関数名の関数を実行することになっていますので、まずはこの関数を探 し、その内容を実行します。
def std_func(self, content):
""" 標準関数の実行
"""
match = re.match('(.+)\((.*)\)', content)
if match:
func_name = match.groups()[0]
if func_name == '表示':
print(match.groups()[1])
def do_content(self, content):
""" 関数の内容実行
"""
match = re.match('(.+)\(.*?\)', content)
if match:
func_name = match.groups()[0]
if func_name in self.funcs:
for todo in self.funcs[func_name]['content']:
self.do_content(todo)
elif func_name in self.STD_FUNCS:
self.std_func(content)
else:
print('Error: 関数が見つかりません。')
def exec(self):
""" 実行関数の実行
"""
if '実行' in self.funcs:
for todo in self.funcs['実行']['content']:
if len(todo) > 0:
self.do_content(todo)
else:
print('Error: 実行関数がありません。')
execを実行するとソースコードの実行関数が呼び出されます。
その後、実行関数内の内容をdo_content関数に渡し、その内容を実行します。
do_content内では標準関数と実装関数を判別し、それぞれの内容を実行します。
完成
上記をまとめると以下のようになります。
import re
### ソースコード
source =
"""
関数 挨拶() -> なし {
表示('こんにちわ');
}
関数 実行() -> なし {
挨拶();
}
"""
### 実行環境クラス
class Execution:
""" 実行環境
"""
# 標準関数
STD_FUNCS = ['表示']
# インプットソース
source = ''
# 実装関数
funcs = {}
def __init__(self, source):
self.source = self.preprocessing(source)
self.funcs = self.analysis_func(self.source)
def preprocessing(self, text):
"""ソース文字列の前処理
"""
text = text.replace('\n', '')
return text
def analysis_func(self, source):
""" 関数構文の検索
"""
funcs = {}
match = re.findall('.+?\s{.+?}', source)
for f in match:
temp = re.match('関数\s(.+)\((.*?)\)\s->(.+){(.+)}', f)
if temp and len(temp.groups()) > 0:
el = temp.groups() funcs[el[0]] = {
'args': el[1],
'return': el[2],
'content': el[3].strip().split(';'),
}
return funcs
def std_func(self, content):
""" 標準関数の実行
"""
match = re.match('(.+)\((.*)\)', content)
if match:
func_name = match.groups()[0]
if func_name == '表示':
print(match.groups()[1])
def do_content(self, content):
""" 関数の内容実行
"""
match = re.match('(.+)\(.*?\)', content)
if match:
func_name = match.groups()[0]
if func_name in self.funcs:
for todo in self.funcs[func_name]['content']:
self.do_content(todo)
elif func_name in self.STD_FUNCS:
self.std_func(content)
else:
print('Error: 関数が見つかりません。')
def exec(self):
""" 実行関数の実行
"""
if '実行' in self.funcs:
for todo in self.funcs['実行']['content']:
if len(todo) > 0:
self.do_content(todo)
else:
print('Error: 実行関数がありません。')
### 実行環境の呼び出し
execution = Execution(source)
execution.exec()
まとめ
今回はPythonを使ってプログラミング言語(の実行環境的なナニカ)をつくってみました。
もっと綺麗に実装しようと思ったら、 compile関数を実装して、その中で書くエラーを吐き出すなど、キリがありませんし、変数や数値計算的なものの実装もしないとプログラミング言語とは呼べません笑
個人的にはプログラミング言語やフレームワークに、その思想やルールといった価値観があり、それ自体に良い悪いを評価するのはナンセンスだと思いますが、必要なら作ってしまおう という発想はエンジニアらしくていいなと思います。
次は「PythonでOS作ってみた」とかになるのでしょうか…。
シンプルに心配です。
最後までお付き合いいただき、ありがとうございました。