バイナリ勉強会 #1
池袋で行われているバイナリ勉強会に初めて、友人と参加させて頂きました。
確か去年も一緒に前期試験が終わった直後に勉強会に参加して、その時は八王子まで出向いた気がします。でもそこでの勉強会は失敗だったんだよな・・・orz
まぁそんな前のことはどうでもいいのですねww
まずは大体半年程度を通してのカリキュラムを見せて頂きました。
基礎編ではBrainf*ckのインタプリタの自作から始まり、Cコンパイラの自作、UNIX V6でカーネル入門等々と続きます。
今日はその第一回なので、最初の最初にGtk#(初耳)でBrainf*ckのインタプリタをC#で作ります。
C#初めてで大丈夫かな・・・?とか思ったのですが、指示に従って進めていくとJavaの先代だけあって結構似通ったところがあって理解も進みます。
ひとまずこれがほとんど完成したところで昼食をはさんで後半戦に望みます。
お次の指示は、自分の好きな言語で何でもいいのでファイルから読み込んでBrainf*ckのインタプリタを作れとのこと。
ここで最初はC言語で作ろうと思ったのですが、気がついたら何故かC++で作ってましたww
今更なのですが、ここでBrainfuckの仕様を説明します。(引用:wikipedia Brainfuck)
処理系は次の要素から成る: Brainfuckプログラム、インストラクションポインタ(プログラム中のある文字を指す)、少なくとも30000個の要素を持つバイトの配列(各要素はゼロで初期化される)、データポインタ(前述の配列のどれかの要素を指す。最も左の要素を指すよう初期化される)、入力と出力の2つのバイトストリーム。
Brainfuckプログラムは、以下の8個の実行可能な命令から成る(他の文字は無視され、読み飛ばされる)。
>:
ポインタをインクリメントする。ポインタをptrとすると、C言語の「ptr++;」に相当する。<:
ポインタをデクリメントする。C言語の「ptr--;
」に相当。+:
ポインタが指す値をインクリメントする。C言語の「(*ptr)++;
」に相当。-:
ポインタが指す値をデクリメントする。C言語の「(*ptr)--;
」に相当。.:
ポインタが指す値を出力に書き出す。C言語の「putchar(*ptr);
」に相当。,:
入力から1バイト読み込んで、ポインタが指す先に代入する。C言語の「*ptr=getchar();
」に相当。[:
ポインタが指す値が0なら、対応する]
の直後にジャンプする。C言語の「while(*ptr){
」に相当。]:
ポインタが指す値が0でないなら、対応する[
(の直後)にジャンプする。C言語の「}
」に相当。
・・・開発始める前にちゃんと仕様読めば良かったなぁ。最初[には絶対に一回は入るように作っちゃうから失敗するんだよねww
そんな感じでなんだかんだで完成したのが以下になります。数ヶ月ぶりのちゃんとしたコーディングなので、完全に感覚が鈍っててコードが汚いのは勘弁してください。
[cpp]
include <iostream>
include <fstream>
include <cstdio>
using namespace std;
void func(char,char,int,int,int*); int loop[128]={};
int main(void){ ifstream ifs; char c,cmd[2048]={},mem[30000]={}; int i=0,cps=0,mps=0,lps=0;
ifs.open("text.bf",ios::in);
if(!ifs){ cout << "ファイルが存在しません。" << endl; return -1; }
for(i=0;!ifs.eof();i++){ ifs.get(c); cmd[i]=c; } while(cps<i-1) func(cmd,mem,&cps,&mps,&lps);
ifs.close(); return 0; }
void func(char cmd,char mem,int cps,int mps,int *lps){ int lev = 0;
switch(cmd[cps]){ case '>': ++mps; break;
case '<': --*mps; break;
case '+': ++mem[*mps]; break;
case '-': --mem[*mps]; break;
case '[': if(mem[mps]>0){ ++lps; loop[lps]=cps; } else{ int lev=1;
do{
++*cps;
switch(cmd[*cps]){
case '[':
++lev;
break;
case ']':
--lev;
break;
}
}while(lev!=0||cmd[*cps]!=']');
} break;
case ']': if(mem[mps]>0) cps=loop[lps]; else --lps; break;
case '.': cout << mem[*mps]; break;
case ',': mem[mps]=getchar(); break; } ++cps; } [/cpp]
ここで初めは","に対して『cin >> mem[*mps]』を使ってたのですが、これだと改行コードが含まれないことが判明したのでgetchar()にしました。 途中break文ごとコメントアウトして何で正常に動かないのだろうとかいうミスをやらかしたりしましたが、結果的に完成したのでよしとしましょう。 ここに完成品を置いておきますね。これはファイル読み込みではなく標準入力にしておきます。
さて、一通り済んだところで残り時間もわずかになりましたが、次の講座のためにMinGWを準備してbinutilsのインストールまでやっておきます。 ・・・ただここで『性能の違い』というものを見せ付けられました。自分のほう(Atom)が先にビルト始めたのに、友人のPC(i5)の方が完全に先に処理を終えるというwwww
まぁこんな感じで久しぶりに良い勉強会に当たったようなので、これから常連さんにでもなろうかと思っています。
これからもよろしくお願いします!