この記事はICTアドベントカレンダー2019の記事です。24日でしたが完全に25日が僕の担当だと勘違いしてました......
はじめに
24日~25日にかけてリモートでやったクリスマスワンナイトハッカソンで、
木更津高専と鳥羽商船の同級生の人たちと3人で「Chrithma」という言語を設計しました。
ハッカソンの概要としては、24日の22:00~5:00までみんなで開発をしてクリぼっちを回避するというものです。
15チーム参加してくれました。
ハッカソンの成果発表の記事はこちらになります
クリスマスにちなんだインタプリタ言語を作った -Qiita
この中で、C++とPythonで書かれたインタプリンタをそれぞれ実装しました。
僕はPython版のインタプリンタを実装したのでそれについて少し詳しく書きます。
言語仕様
文字 | 操作 |
---|---|
C | ポインタをインクリメント |
H | ポインタをデクリメント |
R | ポインタの値をインクリメント |
I | ポインタの値をデクリメント |
S | 現在のポインタの値を出力 |
T | 入力から1バイト読み込みポインタが指す値に代入 |
M | ポインタの値が0なら、次のAまで飛ぶ |
A | ポインタの値が0じゃなかったら、前のMまで飛ぶ |
コード
凄くシンプルです。
import sys
mem = [0 for i in range(10000)]
pointer = 0
program_counter = 0
# コマンドライン引数からファイルを取得
if __name__ == "__main__":
args = sys.argv
path = args[1]
with open(path) as f:
code = f.read()
tape = list(code)
while program_counter < len(tape):
# C
if tape[program_counter] == 'C':
pointer += 1
# H
elif tape[program_counter] == 'H':
pointer -= 1
# R
elif tape[program_counter] == 'R':
mem[pointer] += 1
# I
elif tape[program_counter] == 'I':
mem[pointer] -= 1
# S
elif tape[program_counter] == 'S':
print(chr(mem[pointer]), end="")
# T
elif tape[program_counter] == 'T':
mem[pointer] = int(input())
# M
elif tape[program_counter] == 'M':
if mem[pointer] == 0:
nest = 0
while True:
program_counter += 1
if tape[program_counter] == 'M':
nest += 1
if tape[program_counter] == 'A' and nest == 0:
break
if tape[program_counter] == 'A':
nest -= 1
# A
elif tape[program_counter] == 'A':
if mem[pointer] != 0:
nest = 0
while True:
program_counter -= 1
if tape[program_counter] == 'A':
nest += 1
if tape[program_counter] == 'M' and nest == 0:
break
if tape[program_counter] == 'M':
nest -= 1
program_counter += 1
コードの末尾までプログラムを処理します。
tapeには、コマンドライン引数で得たファイルの中の文字をlistとして格納したcodeという変数が格納されています。
if __name__ == "__main__":
args = sys.argv
path = args[1]
with open(path) as f:
code = f.read()
tape = list(code)
while program_counter < len(tape):
各要素の処理。
そのままですが....
C
もしCという文字があったら変数pointerの値をインクリメントしています。
if tape[program_counter] == 'C':
pointer += 1
H
こっちはさっきの逆でHという文字があったら変数pointerの値をデクリメントしてます。
elif tape[program_counter] == 'H':
pointer -= 1
R
Rとい文字があったら、メモリのpointerをインクリメントしています。メモリはmemという変数であらわしています。
elif tape[program_counter] == 'R':
mem[pointer] += 1
I
Iという文字があったらmemのpointerっをデクリメントします。
elif tape[program_counter] == 'I':
mem[pointer] -= 1
S
Sは、現在のpointerの値を出力します。
chr(mem[pointer])
で10進数のASCIIコードから文字を変換して出力します。ASCIIコードはmemのpointerの値が示します。
例えば値が65なら「A」が出力されます。値65を示すには、Rを65回書くだけです。簡単ですね!
end=""
は、出力しても改行をしないようにという処理です。
elif tape[program_counter] == 'S':
print(chr(mem[pointer]), end="")
T
Tは入力ですね。
memory[pointer]
に対し、入力したデータを整数値に変換し代入しています。
elif tape[program_counter] == 'T':
mem[pointer] = int(input())
M
Mはポインタの値が0なら次のAまで飛ぶという処理でした。
if mem[pointer] == 0:
また、
MM<処理>AAのように入れ子構造になってた場合の処理のためにnest = 0
でネスト構造をカウントする変数を用意しました。
あとはwhileで回すだけです。
elif tape[program_counter] == 'M':
if mem[pointer] == 0:
nest = 0
while True:
program_counter += 1
if tape[program_counter] == 'M':
nest += 1
if tape[program_counter] == 'A' and nest == 0:
break
if tape[program_counter] == 'A':
nest -= 1
A
ポインタの値が0じゃなかったら、前のMまで飛ぶという処理です。さっきの逆ですね。
elif tape[program_counter] == 'A':
if mem[pointer] != 0:
nest = 0
while True:
program_counter -= 1
if tape[program_counter] == 'A':
nest += 1
if tape[program_counter] == 'M' and nest == 0:
break
if tape[program_counter] == 'M':
nest -= 1
end
最後にプログラムカウンタをインクリメントします
program_counter += 1
所感
はじめてインタプリンタを実装しましたが、思いの外コード量が少なくて驚きました。
バグがあるかもしれませんが許してください()
そしてこれは24日の記事のはずですが完全にわすれてて25日になったことも許してください。
さらに25日の23時からだらだらと書いてたらいつのまにか日付がかわって26日になってたことも許してくださいいいい
ソースコードを張っておきます。ドキュメントもここにあります
https://github.com/FlexiblePrintedCircuits/Christma