Brainfuckコンパイラ自作シリーズ 第4回:Cコード生成と実行
はじめに
本記事では、BrainfuckコンパイラのCコード生成部分を実装し、BrainfuckコードをC言語に変換してコンパイル・実行するまでの流れを解説します。
前回までに 字句解析(Lexing) と 構文解析(Parsing) を実装しました。今回はその解析結果をもとに C言語のソースコードを生成し、コンパイルして実行する までをカバーします。
1. BrainfuckからCコードへの変換
Brainfuckの各命令をC言語に変換する方法を考えます。以下のように対応させるのが一般的です:
Brainfuck | C言語 |
---|---|
> |
ptr++; |
< |
ptr--; |
+ |
(*ptr)++; |
- |
(*ptr)--; |
. |
putchar(*ptr); |
, |
*ptr = getchar(); |
[ |
while (*ptr) { |
] |
} |
この変換ルールを基に generator.cpp
を実装します。
2. Cコード生成の実装
2.1 generator.h
の作成
まず、Cコード生成を担当する generator.h
を作成します。
#ifndef GENERATOR_H #define GENERATOR_H #include <string> // BrainfuckコードをC言語コードに変換する関数 std::string generateCCode(const std::string& bfCode); #endif // GENERATOR_H
2.2 generator.cpp
の実装
次に、BrainfuckのコードをCコードへ変換するロジックを generator.cpp
に実装します。
#include "generator.h" #include <iostream> #include <sstream> std::string generateCCode(const std::string& bfCode) { std::ostringstream cCode; cCode << "#include <stdio.h>\n"; cCode << "int main() {\n"; cCode << " unsigned char tape[30000] = {0};\n"; cCode << " unsigned char *ptr = tape;\n"; for (char c : bfCode) { switch (c) { case '>': cCode << " ptr++;\n"; break; case '<': cCode << " ptr--;\n"; break; case '+': cCode << " (*ptr)++;\n"; break; case '-': cCode << " (*ptr)--;\n"; break; case '.': cCode << " putchar(*ptr);\n"; break; case ',': cCode << " *ptr = getchar();\n"; break; case '[': cCode << " while (*ptr) {\n"; break; case ']': cCode << " }\n"; break; default: break; // 無視する } } cCode << " return 0;\n"; cCode << "}\n"; return cCode.str(); }
3. Cコードを生成しコンパイル・実行する
次に、メインプログラム main.cpp
からこの generateCCode
を呼び出し、C言語のファイルを作成してコンパイルします。
#include <iostream> #include <fstream> #include <cstdlib> #include "generator.h" int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "使い方: " << argv[0] << " <Brainfuckファイル>\n"; return 1; } // Brainfuckファイルを読み込む std::ifstream bfFile(argv[1]); if (!bfFile) { std::cerr << "ファイルを開けません: " << argv[1] << "\n"; return 1; } std::string bfCode((std::istreambuf_iterator<char>(bfFile)), std::istreambuf_iterator<char>()); // Cコードを生成 std::string cCode = generateCCode(bfCode); // C言語のファイルを書き出す std::ofstream cFile("output.c"); cFile << cCode; cFile.close(); // Cコードをコンパイル(GCCを使用) int result = system("gcc output.c -o output"); if (result != 0) { std::cerr << "コンパイルに失敗しました\n"; return 1; } std::cout << "コンパイル成功: 実行ファイル 'output' が作成されました。\n"; std::cout << "実行するには './output' を実行してください。\n"; return 0; }
4. 動作確認
4.1 簡単な Brainfuck プログラムの用意
まず、Hello, World! を表示するBrainfuckプログラムを作成します。
hello.bf
:
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
4.2 コンパイルと実行
ターミナルで以下のコマンドを実行します。
$ g++ main.cpp generator.cpp -o bf_compiler
$ ./bf_compiler hello.bf
成功すると、output.c
が生成され、GCCでコンパイルされます。
$ ./output Hello, World!
5. まとめと次回予告
今回は、BrainfuckのコードをC言語に変換し、コンパイルして実行する流れを実装しました。
これにより、Brainfuckコンパイラの基本的な仕組みが完成しました。
次回は、最適化とエラー処理 に焦点を当て、以下の内容を実装します。
++++++
を+=6
に変換する最適化- 無駄な移動 (
><
や<>
など) の削除 - 構文エラー(
[
の対応漏れ)のチェック - 実用的なBrainfuckプログラムのテスト
この最適化を加えることで、Brainfuckコンパイラをより実用的なものに仕上げていきます。
参考文献
次回もお楽しみに!