TechCraft – エンジニアのためのスキルアップメモ

エンジニアのスキルアップを少しでも加速する技術ブログ

Brainfuck コンパイラの自作 第2回

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 - コンパイラ入門(英語)

次回も楽しみに!