Brainfuck コンパイラの自作 第2回 - 字句解析(Lexing)の実装
1. はじめに
前回の記事では、Brainfuck のコンパイラ設計を行い、C++ で開発環境を整えました。
techcraft.hatenablog.com 今回は、字句解析(Lexing) の実装を行い、Brainfuck のコードを解析しやすい形に変換します。
2. 字句解析(Lexing)とは?
字句解析(Lexing)は、ソースコードを意味のある単位(トークン) に分解するプロセスです。
Brainfuck のトークン
Brainfuck の命令は 8種類 のみですが、それ以外の文字(改行やコメントなど)も含まれる可能性があるため、解析時に無視する必要があります。
コマンド | 説明 |
---|---|
> |
ポインタを右へ移動 |
< |
ポインタを左へ移動 |
+ |
ポインタの指す値を1増加 |
- |
ポインタの指す値を1減少 |
. |
ポインタの指す値をASCII文字として出力 |
, |
1文字入力し、ポインタの指すセルに格納 |
[ |
もし現在の値が0なら対応する ] へジャンプ |
] |
もし現在の値が0でなければ対応する [ へジャンプ |
字句解析の目的 - Brainfuck の命令以外の文字を無視する(コメントや空白) - トークン(命令のリスト)を作成し、後の処理を簡単にする
3. 字句解析の実装
C++ で Lexer
クラスを作成し、ソースコードを解析する処理を実装します。
プロジェクト構成
brainfuck_compiler/ ├── main.cpp (エントリポイント) ├── lexer.cpp (字句解析の実装) ├── lexer.hpp (Lexer クラスの定義) ├── Makefile (ビルド用)
1. lexer.hpp
(Lexer クラスの定義)
#ifndef LEXER_HPP #define LEXER_HPP #include <vector> #include <string> class Lexer { public: explicit Lexer(const std::string& source); std::vector<char> tokenize(); // トークンリストを返す private: std::string source; }; #endif
2. lexer.cpp
(Lexer クラスの実装)
#include "lexer.hpp" #include <iostream> // コンストラクタ:ソースコードを受け取る Lexer::Lexer(const std::string& source) : source(source) {} // トークン化処理 std::vector<char> Lexer::tokenize() { std::vector<char> tokens; for (char c : source) { // Brainfuck の命令のみを抽出 if (c == '>' || c == '<' || c == '+' || c == '-' || c == '.' || c == ',' || c == '[' || c == ']') { tokens.push_back(c); } } return tokens; }
3. main.cpp
(Lexer を使って解析する)
#include <iostream> #include <fstream> #include "lexer.hpp" int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "使い方: " << argv[0] << " <brainfuckファイル>\n"; return 1; } // ファイルを読み込む std::ifstream file(argv[1]); if (!file) { std::cerr << "ファイルを開けませんでした: " << argv[1] << "\n"; return 1; } std::string code((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); // 字句解析を行う Lexer lexer(code); std::vector<char> tokens = lexer.tokenize(); // トークンの確認 std::cout << "トークン化されたBrainfuckコード:\n"; for (char token : tokens) { std::cout << token; } std::cout << std::endl; return 0; }
4. 実行方法
1. コンパイル
g++ main.cpp lexer.cpp -o bf_lexer
2. テスト用の Brainfuck コード(hello.bf
)
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++. >++.<<+++++++++++++++.>.+++.------.--------.>+.>.
3. 実行
./bf_lexer hello.bf
4. 出力結果
トークン化されたBrainfuckコード: ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++. >++.<<+++++++++++++++.>.+++.------.--------.>+.>.
これで、Brainfuck の命令だけが抽出されていることが確認できます。
5. まとめと次回の予定
今回は、Brainfuck の字句解析(Lexing)を実装しました。 - Lexing とは何か? - Brainfuck の命令をトークン化 - C++ で Lexer クラスを実装 - ファイルから Brainfuck コードを読み取り、解析を実行
次回予定
次回は、構文解析(Parsing) を実装し、[
と ]
の対応を管理する処理を作ります。
- [
と ]
のペアの確認
- ジャンプテーブルの作成
- 文法エラー(対応しない [
]
)の検出
次に学ぶべきこと
参考リンク - Brainfuck Wikipedia - コンパイラ入門(英語)
次回も楽しみに!