Help us understand the problem. What is going on with this article?

ClojureでBrainfuckを実装する

勉強がてらClojureでBrainfuckを実装してみます。

ソース全文
https://github.com/akthrms/brainfuck

Clojureとは

Java仮想マシン(JVM)で動作するLisp言語の方言の1つです。

Brainfuckとは

8種類の命令のみで構成された難読プログラミング言語です。

文字 動作 C言語
> ポインタをインクリメントする。 ptr++;
< ポインタをデクリメントする。 ptr--;
+ ポインタが指す値をインクリメントする。 (*ptr)++;
- ポインタが指す値をデクリメントする。 (*ptr)--;
. ポインタが指す値を出力に書き出す。 putchar(*ptr);
, 入力から1バイト読み込んで、ポインタが指す先に代入する。 *ptr = getchar();
[ ポインタが指す値が0なら、対応する ] の直後にジャンプする。 while (*ptr) {
] ポインタが指す値が0でないなら、対応する [ (の直後)にジャンプする。 }

Hello World! はこんな感じです。

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.

実装

メモリとポインタ

それぞれベクタと数値で管理します。
ついでに処理対象の文字位置も保持しておきます。

; メモリ
(def memory (atom (vec (repeat 30000 0))))

; ポインタ
(def pointer (atom 0))

; 文字位置
(def index (atom 0))

命令

マルチメソッドを用いて実装しています。
[] がちょっとややこしいです。( , の実装は微妙かも)

; ポインタが指す値を取得する
(defn- get-reference []
  (nth @memory @pointer))

(defmulti command (fn [file-chars idx] (identity (nth file-chars idx))))

; ポインタを加算する
(defmethod command \> [& _]
  (swap! pointer inc))

; ポインタを減算する
(defmethod command \< [& _]
  (swap! pointer dec))

; ポインタが指す値を加算する
(defmethod command \+ [& _]
  (swap! memory assoc @pointer (inc (get-reference))))

; ポインタが指す値を減算する
(defmethod command \- [& _]
  (swap! memory assoc @pointer (dec (get-reference))))

; ポインタが指す値を出力する
(defmethod command \. [& _]
  (print (char (get-reference))))

; ポインタが指す値に入力する
(defmethod command \, [& _]
  (swap! memory assoc @pointer (read)))

; ループを開始する
(defmethod command \[ [file-chars _]
  (when (zero? (get-reference))
    (loop [bracket-count 1]
      (when (> bracket-count 0)
        (do
          (swap! index inc)
          (case (nth file-chars @index)
            \[ (recur (inc bracket-count))
            \] (recur (dec bracket-count))
            (recur bracket-count)))))))

; ループを終了する
(defmethod command \] [file-chars _]
  (when (not (zero? (get-reference)))
    (loop [bracket-count 1]
      (when (> bracket-count 0)
        (do
          (swap! index dec)
          (case (nth file-chars @index)
            \] (recur (inc bracket-count))
            \[ (recur (dec bracket-count))
            (recur bracket-count)))))))

; それ以外はスキップする
(defmethod command :default [& _])

実行

対象文字の命令を実行しつつ、文字位置をインクリメントします。

; 実行
(defn exec [file-chars]
  (loop [idx @index]
    (when (> (count file-chars) idx)
      (command file-chars idx)
      (swap! index inc)
      (recur @index))))

(exec (vec ">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++."))
Hello World!
=> nil

参考

[C#] Brainfuckのインタプリタ実装方法

akthrms
関数型プログラミングに興味あります
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした