Skip to content

Instantly share code, notes, and snippets.

@igorw
Last active August 29, 2015 14:15
Show Gist options
  • Save igorw/e63958a960bfdd42a08f to your computer and use it in GitHub Desktop.
Save igorw/e63958a960bfdd42a08f to your computer and use it in GitHub Desktop.
bf

bf

A Clojure library designed to ... well, that part is up to you.

Usage

$ lein run ">++++++++[<+++++++++>-]<.>>+>+>++>[-]+<[>[->+<<++++>]<<]>.+++++++..+++.>>+++++++.<<<[[-]<[-]>]<+++++++++++++++.>>.+++.------.--------.>>+.>++++."
Hello World!

License

Copyright © 2015 FIXME

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

(ns bf.core
(require [clojure.edn :as edn]))
(declare run)
(defn step [code state]
(let [current-field (get-in state [:heap (:p state)] 0)
instr (get code (:ip state))
instr (if (symbol? instr) (name instr) instr)]
(cond
(= instr ">") (update-in state [:p] inc)
(= instr "<") (update-in state [:p] dec)
(= instr "+") (update-in state [:heap (:p state)] (fnil inc 0))
(= instr "-") (update-in state [:heap (:p state)] (fnil dec 0))
(= instr ".")
(let [current-char (char current-field)]
(print current-char)
state)
(= instr ",")
(let [current-char (.read *in*)]
(assoc-in state [:heap (:p state)] (if (= current-char -1) 0 current-char)))
(vector? instr)
(if (zero? current-field)
(update-in state [:ip] inc)
(loop [vec-state (assoc state :ip 0)]
(let [vec-state (run instr vec-state)
current-field (get-in vec-state [:heap (:p vec-state)] 0)]
(if (zero? current-field)
(assoc vec-state :ip (:ip state))
(recur (assoc vec-state :ip 0))))))
(nil? instr) nil
:else (throw (Exception. (str "unknown instruction " (pr-str instr)))))))
(defn run
([code]
(run code {:heap {} :ip 0 :p 0}))
([code state]
(dissoc
(loop [state state]
(if-let [new-state (step code state)]
(recur (if (= (:ip new-state) (:ip state))
(update-in new-state [:ip] inc)
new-state))
state))
:ip)))
(defn -main [& args]
(doseq [arg args]
(let [arg-chars (map str (seq arg))
arg-chars (map #(if (or (= % "[") (= % "]")) % (pr-str %)) arg-chars)]
(run (edn/read-string (str "[" (clojure.string/join " " arg-chars) "]"))))))
(ns bf.core-test
(:require [clojure.test :refer :all]
[bf.core :refer :all]))
(defn seq->map [xs]
(apply hash-map (interleave (range 0 (count xs)) xs)))
(defmacro expect-output [expected & body]
`(let [s# (new java.io.StringWriter)]
(binding [*out* s#]
(let [res# ~@body]
(is (= ~expected (str s#)))
res#))))
(deftest empty-test
(testing "empty program"
(is (= {:heap (seq->map []) :p 0} (run [])))))
(deftest >-test
(testing ">"
(is (= {:heap (seq->map []) :p 1} (run '[>])))))
(deftest >>-test
(testing ">>"
(is (= {:heap (seq->map []) :p 2} (run '[> >])))))
(deftest +-test
(testing "+"
(is (= {:heap (seq->map [1]) :p 0} (run '[+])))))
(deftest ++-test
(testing "++"
(is (= {:heap (seq->map [2]) :p 0} (run '[+ +])))))
(deftest +--test
(testing "+-"
(is (= {:heap (seq->map [0]) :p 0} (run '[+ -])))))
(deftest ><-test
(testing "><"
(is (= {:heap (seq->map []) :p 0} (run '[> <])))))
(deftest inc-10-test
(testing "inc 10"
(is (= {:heap (seq->map [10]) :p 0} (run '[+ + + + + + + + + +])))))
(deftest print-newline-test
(testing "print newline"
(expect-output "\n"
(is (= {:heap (seq->map [10]) :p 0} (run '[+ + + + + + + + + + .]))))))
(deftest .-test
(testing "null byte"
(expect-output (str (char 0))
(is (= {:heap (seq->map []) :p 0} (run '[.]))))))
(deftest input-newline-test
(testing "input newline"
(with-in-str "\n"
(is (= {:heap (seq->map [10]) :p 0} (run '[","]))))))
(deftest input-hi-test
(testing "input hi"
(with-in-str "hi\n"
(is (= {:heap (seq->map [104 105 10]) :p 2} (run '["," > "," > ","]))))))
(deftest if-skip-test
(testing "if skip"
(is (= {:heap (seq->map [1]) :p 0} (run '[[+ +] +])))))
(deftest if-single-dec-test
(testing "if skip"
(is (= {:heap (seq->map [0]) :p 0} (run '[+ [-]])))))
(deftest countdown-test
(testing "countdown"
(is (= {:heap (seq->map [0]) :p 0} (run '[+ + + [-]])))))
(deftest nested-loop-test
(testing "nested loop"
(is (= {:heap (seq->map [0]) :p 0} (run '[+ + + [+ [- -]]])))))
(deftest hello-world-test
(testing "hello world"
(expect-output "Hello World!\n"
(is (= {:heap (seq->map [0 0 72 100 87 33 10]) :p 6}
(run '[+ + + + + + + + [> + + + + [> + + > + + + > + + + > + < < < < -] > + > + > - > > + [<] < -] > > . > - - - . + + + + + + + . . + + + . > > . < - . < . + + + . - - - - - - . - - - - - - - - . > > + . > + + .]))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment