きっかけ
c++でコード譜を読み込んでwavを生成するアプリを作ろうとして挫折し、もっと書きやすくてネイティブコードを生成する言語を探していたところ、D言語を見つけた。
#必要なもの
- D言語コンパイラ(DMD | GDC | LLDC)
- 今回はDMDを使用
$ brew install dmd
#実装
-
Brainf*ckという名前が嫌なのとそのまま実装するのも面白くないので、使う文字を変えて独自言語を作成。その名もSugoilang.
- '>' -> "すごい"
- '<' -> "ものすごい"
- '+' -> "尋常じゃない"
- '-' -> "とにかくすごい"
- '.' -> "すごすぎる
- ',' -> "常識を超えている"
- '[' -> "やばい"
- ']' -> "やばすぎる"
-
ポインタが指すメモリの値を0に初期化する命令を追加 -> "!!!!!!"
(注:すいません。多重ループはできません。)
###では、すごい言語の実装開始!!
sugoi.d
enum Token{
PIncr,PDecr,VIncr,VDecr,Put,Get,LoopL,LoopR,Init
}
- トークンを定義。上から順に対応。
sugoi.d
string sugoi="すごい";
string mono="ものすごい";
string jinjo="尋常じゃない";
string tonikaku="とにかくすごい";
string sugosugiru="すごすぎる";
string josiki="常識を超えている";
string yabai="やばい";
string yabasugiru="やばすぎる";
string bikkuri="!!!!!!";
string last="また、すごすぎるコードを書いてしまった...";
- Dではstringはimmutable char[]のalias。
- lastはファイルの最後の文字列比較の時にバッファオーバーランを防ぐためにつける。
###パーサーを作成
parse.d
import sugoi;
void parse(ref char[] buff, ref Token[] token){
int i;
while(I<buff.length){
if(buff[i..i+sugoi.sugoi.length]==sugoi.sugoi){
token~=Token.Incr;
i+=sugoi.sugoi.length;
}
else if(buff[i..i+mono.length]==mono){
token~=Token.Decr;
i+=mono.length;
}
/*
.
. 省略
.
*/
else if(buff[i..i+bikkuri.length]==bikkuri{
token~=Token.Init;
i+=bikkuri,length;
}
else if(buff[i..i+last.length]==last)
break;
else
i++;
}
}
- 1行目ではモジュールをインポートしている。モジュールは "module XXX" で宣言。
宣言がなければファイル名がモジュール名になる。 - "ref" が参照。
- モジュール名と他の名前がかぶるとモジュール名を優先するのでsugoi.sugoiみたいにしなければならない。
- "a[i..j]" で文字列のiからjの一つ前までをスライス。他にも'$'が配列全体の長さとして定義されている。
- '~'は配列結合演算子。"~="で宣言時に長さを指定しない動的配列の末尾に要素を追加。
###実行関数
carry.d
import std.stdio;
import sugoi;
void carry(ref Token[] token){
int[30000] stack;
int iter;
int tmpL;//LoopLの場所を保管
bool skip=false;//LoopLで0の時にスキップ
int i;
while(i<token.length){
if(!skip){
if(token[i]==Token.PIncr)
iter++;
else if(token[i]==Token.Pdecr)
iter--;
else if(token[i]==Token.VIncr)
stack[iter]++;
else if(token[i]==Token.VDecr)
stack[iter]--;
else if(token[i]==Token.Put)
write(cast(char) stack[iter]);
else if(token[i]==Token.Get)
stack[iter]=getchar();
else if(token[i]==Token.Init)
stack[iter]=0;
else if(token[i]==Token.LoopL){
if(stack[iter]==0)
skip=true;
else
tmpL=i;
}
else if(token[i]==Token.LoopR
i=tmpL-1;
}
else if(token[i]==token.LoopR)
skip=false;
i++;
}
}
- 全てのデフォルト型の変数は宣言時に初期化される。
- write()で改行なし出力。cast(char)でキャスト。
main.d
import std.stdio;
import sugoi;
import parse;
import carry;
void main(string[] args){
auto f=File(args[1],"r");
Token[] token;
char[] buff;
foreach(line; f.byLine)
buff~=line;
parse.parse(buff,token);
carry.carry(token);
}
- "byLine"はc++のgetlineと同じで改行を含まない。あとイテレータの"line"に"auto"をつけるとエラーになった。何で?
###それではコンパイルしてみよ〜!!
$ dmd main.d sugoi.d parse.d carry.d
- 何と、全部一度にコンパイルしないとダメみたいです...
sugoi.sg
尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常
じゃない尋常じゃない尋常じゃないやばいすごい尋常じゃない尋常じゃない尋常じゃ
ない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃないすごい尋常じ
ゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃな
い尋常じゃない尋常じゃない尋常じゃない尋常じゃないすごい尋常じゃない尋常じゃ
ない尋常じゃない尋常じゃない尋常じゃないものすごいものすごいものすごいとにか
くすごいやばすぎるすごいすごすぎるすごい尋常じゃない尋常じゃないすごすぎる尋
常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じ
ゃないすごすぎるすごすぎる尋常じゃない尋常じゃない尋常じゃないすごすぎるすご
いとにかくすごいとにかくすごいとにかくすごいとにかくすごいとにかくすごいとに
かくすごいとにかくすごいとにかくすごいとにかくすごいとにかくすごいとにかくす
ごいとにかくすごいとにかくすごいすごすぎるものすごいものすごい尋常じゃない尋
常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じ
ゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃない尋常じゃな
い尋常じゃないすごすぎるすごいすごすぎる尋常じゃない尋常じゃない尋常じゃない
すごすぎるとにかくすごいとにかくすごいとにかくすごいとにかくすごいとにかくす
ごいとにかくすごいすごすぎるとにかくすごいとにかくすごいとにかくすごいとにか
くすごいとにかくすごいとにかくすごいとにかくすごいとにかくすごいすごすぎるす
ごい尋常じゃないすごすぎるすごすぎる
また、すごすぎるコードを書いてしまった...
サンプルです。
実行
$ ./main sugoi.sg
結果
Hello,World!(ユーザー名)
#あとがき
D言語はこれからも流行らないであろう...