【C++】難解プログラミング言語を作ろう!【brainf*ckベース】
難解プログラミング言語
難解(難読)プログラミング言語 (esolang: Esoteric programming language)とは,その名の通りとても読みづらいプログラミング言語を指します.
いわゆる,ジョーク言語です.
最も有名なのはBrainfuckだと思いますが,「Hello, world!」ならこんな感じです.
1 2 | +++++++++[->++++++++>+++++++++++>+++++<<<]>.>++.+++++++..+++.>-.--------- ---.<++++++++.--------.+++.------.--------.>+. |
「難解」と言われるのもなんとなくわかります.
ただ,Brainfuckの命令はとてもシンプルで8つの命令で成り立っており,チューリング完全な言語です.
したがって難解プログラミング言語初心者にはもってこいなわけです(?)
まずはBrainfuckのインタプリタを作る
Brainfuckについて知らない方は,僕が昔書いた記事を読んでください.
ともかく,8つの命令に対してポインタを操作していけば良いので,そんなに難しくないはずです.
そして,きっと誰かすでに綺麗なコードを実装しているだろうという甘い考えのもと,とりあえず実装しました.笑
といっても,最終的にオリジナルな難解プログラミング言語を作るわけなので,それがしやすいように修正はします.
とりあえずこんな感じ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #include <iostream> #include <stack> #define MEMORY_SIZE 1024 #define PTR_INCREMENT '>' #define PTR_DECREMENT '<' #define BYTE_INCREMENT '+' #define BYTE_DECREMENT '-' #define OUTPUT '.' #define INPUT ',' #define LOOP_START '[' #define LOOP_END ']' using namespace std; int main(int argc, char* argv[]) { /* memory of array */ char memory[MEMORY_SIZE]; /* source code */ char *source = nullptr; /* recurrent pointer */ unsigned short ptr = 0; /* recurrent pointer of the source code */ int order_ptr = 0; /* end of pointer of the source code */ int order_end = 0; /* stack of multi-loop */ stack<int> loops; /* initialize memory */ memset(memory, 0, sizeof(memory)); // check command line arguments if(argc < 2){ cout << "Oops! There are no-arguments." << endl; return 0; } /* get filename */ char *filename = argv[1]; FILE *fp; /* open file */ if (!(fp = fopen(filename, "rb"))){ cout << "ERROR: Not found the designated file." << endl; return -1; } /* get code */ if(fseek(fp, 0, SEEK_END) == 0){ int size = ftell(fp); if(fseek(fp, 0, SEEK_SET) == 0){ source = new char[size]; order_end = fread(source, 1, size, fp); } } fclose(fp); /* main loop */ while(order_ptr < order_end){ /* order switch */ switch(source[order_ptr]){ case PTR_INCREMENT: ptr++; break; case PTR_DECREMENT: ptr--; break; case BYTE_INCREMENT: (memory[ptr])++; break; case BYTE_DECREMENT: (memory[ptr])--; break; case OUTPUT: putchar(memory[ptr]); break; case INPUT: memory[ptr] = getchar(); break; case LOOP_START: loops.push(order_ptr); if(memory[ptr] == 0){ int depth = 1; while(depth > 0){ order_ptr++; if(order_ptr >= order_end){ cout << "ERROR: Not found \'" << LOOP_END << "\' ." <<endl; return -1; } switch(source[order_ptr]){ case LOOP_START: depth++; break; case LOOP_END: depth--; break; } } loops.pop(); } break; case LOOP_END: if(loops.empty()){ cout << "ERROR: Not found \'" << LOOP_START << "\'." <<endl; return -1; } order_ptr = loops.top() - 1; loops.pop(); break; default: /* characters except the orders are handled as a comment */ break; } /* increment the recurrent order pointer */ order_ptr++; } delete[] source; return 0; } |
コードの冒頭で,8つの命令を定義しているので,簡単に好きなものに変えられます.
コードやサンプルコードはGithubにあげています.
とりあえずBrainfuckを実行してみる
先のGithubのコードに沿っていきます.
大したコードじゃないくせにCmakeListsを無駄に用意しているので使ってください.笑
(CLionが勝手に作ってくれるので...)
1 2 3 4 5 6 7 | $ cd PROJECT_RROOT $ mkdir build $ cd build $ cmake .. $ make $ ./bfi ../hello.bf Hello, world! |
良い感じです.
適当に命令をいじってみる
それでは今回の本命である,オリジナルの難解プログラミング言語を作ってみましょう.
ためしに,
1 2 3 4 5 6 7 8 | #define PTR_INCREMENT '>' #define PTR_DECREMENT '<' #define BYTE_INCREMENT '+' #define BYTE_DECREMENT '-' #define OUTPUT '.' #define INPUT ',' #define LOOP_START '[' #define LOOP_END ']' |
を
1 2 3 4 5 6 7 8 | #define PTR_INCREMENT 'a' #define PTR_DECREMENT 'b' #define BYTE_INCREMENT 'c' #define BYTE_DECREMENT 'd' #define OUTPUT 'e' #define INPUT 'f' #define LOOP_START 'g' #define LOOP_END 'h' |
にしてみます.
するとさっきの「Hello, world」コードは以下のように書かなければいけません.
1 2 | cccccccccgdaccccccccacccccccccccacccccbbbhaeacceccccccceeccceadeddddddddd dddebcccccccceddddddddeccceddddddeddddddddeace |
オリジナルの暗号 (esolang) ができました!
コンパイルし直してインタプリタに通すと,しっかり「Hello, world」が出力されます.
おわりに
今回は「オリジナルの難解で変態なプログラミング言語作りてぇ!」という皆さんに記事を書きました.
これで皆さんも難解プログラミング言語を作りましょう.